Fair retry interval.

This commit is contained in:
Nuno Cruces
2024-12-13 10:23:43 +00:00
parent 844fab4167
commit 2bb1c8c795

44
conn.go
View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"math" "math"
"math/rand"
"net/url" "net/url"
"strings" "strings"
"time" "time"
@@ -24,7 +25,6 @@ type Conn struct {
interrupt context.Context interrupt context.Context
pending *Stmt pending *Stmt
stmts []*Stmt stmts []*Stmt
timer *time.Timer
busy func(context.Context, int) bool busy func(context.Context, int) bool
log func(xErrorCode, string) log func(xErrorCode, string)
collation func(*Conn, string) collation func(*Conn, string)
@@ -36,7 +36,8 @@ type Conn struct {
rollback func() rollback func()
arena arena arena arena
handle uint32 kickoff time.Time
handle uint32
} }
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI]. // Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
@@ -389,38 +390,17 @@ func (c *Conn) BusyTimeout(timeout time.Duration) error {
} }
func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (retry uint32) { func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (retry uint32) {
// https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.interrupt.Err() == nil { if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.interrupt.Err() == nil {
const delays = "\x01\x02\x05\x0a\x0f\x14\x19\x19\x19\x32\x32\x64" switch {
const totals = "\x00\x01\x03\x08\x12\x21\x35\x4e\x67\x80\xb2\xe4" case count == 0:
const ndelay = int32(len(delays) - 1) c.kickoff = time.Now()
case time.Since(c.kickoff) >= time.Duration(tmout)*time.Millisecond:
var delay, prior int32 return 0
if count <= ndelay {
delay = int32(delays[count])
prior = int32(totals[count])
} else {
delay = int32(delays[ndelay])
prior = int32(totals[ndelay]) + delay*(count-ndelay)
}
if delay = min(delay, tmout-prior); delay > 0 {
delay := time.Duration(delay) * time.Millisecond
if c.interrupt.Done() == nil {
time.Sleep(delay)
return 1
}
if c.timer == nil {
c.timer = time.NewTimer(delay)
} else {
c.timer.Reset(delay)
}
select {
case <-c.interrupt.Done():
c.timer.Stop()
case <-c.timer.C:
return 1
}
} }
const sleepIncrement = 4*1024*1024 - 1 // power of two, ~4ms
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
return 1
} }
return 0 return 0
} }