WIP update deps, sql builder instead of jet

This commit is contained in:
2025-03-10 10:29:18 -04:00
parent cb3b1a429c
commit 13747c2118
87 changed files with 5208 additions and 2523 deletions

View File

@@ -1,8 +1,10 @@
// Package clockwork contains a simple fake clock for Go.
package clockwork
import (
"context"
"sort"
"errors"
"slices"
"sync"
"time"
)
@@ -14,49 +16,18 @@ type Clock interface {
Sleep(d time.Duration)
Now() time.Time
Since(t time.Time) time.Duration
Until(t time.Time) time.Duration
NewTicker(d time.Duration) Ticker
NewTimer(d time.Duration) Timer
AfterFunc(d time.Duration, f func()) Timer
}
// FakeClock provides an interface for a clock which can be manually advanced
// through time.
//
// FakeClock maintains a list of "waiters," which consists of all callers
// waiting on the underlying clock (i.e. Tickers and Timers including callers of
// Sleep or After). Users can call BlockUntil to block until the clock has an
// expected number of waiters.
type FakeClock interface {
Clock
// Advance advances the FakeClock to a new point in time, ensuring any existing
// waiters are notified appropriately before returning.
Advance(d time.Duration)
// BlockUntil blocks until the FakeClock has the given number of waiters.
BlockUntil(waiters int)
}
// NewRealClock returns a Clock which simply delegates calls to the actual time
// package; it should be used by packages in production.
func NewRealClock() Clock {
return &realClock{}
}
// NewFakeClock returns a FakeClock implementation which can be
// manually advanced through time for testing. The initial time of the
// FakeClock will be the current system time.
//
// Tests that require a deterministic time must use NewFakeClockAt.
func NewFakeClock() FakeClock {
return NewFakeClockAt(time.Now())
}
// NewFakeClockAt returns a FakeClock initialised at the given time.Time.
func NewFakeClockAt(t time.Time) FakeClock {
return &fakeClock{
time: t,
}
}
type realClock struct{}
func (rc *realClock) After(d time.Duration) <-chan time.Time {
@@ -75,6 +46,10 @@ func (rc *realClock) Since(t time.Time) time.Duration {
return rc.Now().Sub(t)
}
func (rc *realClock) Until(t time.Time) time.Duration {
return t.Sub(rc.Now())
}
func (rc *realClock) NewTicker(d time.Duration) Ticker {
return realTicker{time.NewTicker(d)}
}
@@ -87,7 +62,14 @@ func (rc *realClock) AfterFunc(d time.Duration, f func()) Timer {
return realTimer{time.AfterFunc(d, f)}
}
type fakeClock struct {
// FakeClock provides an interface for a clock which can be manually advanced
// through time.
//
// FakeClock maintains a list of "waiters," which consists of all callers
// waiting on the underlying clock (i.e. Tickers and Timers including callers of
// Sleep or After). Users can call BlockUntil to block until the clock has an
// expected number of waiters.
type FakeClock struct {
// l protects all attributes of the clock, including all attributes of all
// waiters and blockers.
l sync.RWMutex
@@ -96,11 +78,27 @@ type fakeClock struct {
time time.Time
}
// NewFakeClock returns a FakeClock implementation which can be
// manually advanced through time for testing. The initial time of the
// FakeClock will be the current system time.
//
// Tests that require a deterministic time must use NewFakeClockAt.
func NewFakeClock() *FakeClock {
return NewFakeClockAt(time.Now())
}
// NewFakeClockAt returns a FakeClock initialised at the given time.Time.
func NewFakeClockAt(t time.Time) *FakeClock {
return &FakeClock{
time: t,
}
}
// blocker is a caller of BlockUntil.
type blocker struct {
count int
// ch is closed when the underlying clock has the specificed number of blockers.
// ch is closed when the underlying clock has the specified number of blockers.
ch chan struct{}
}
@@ -111,23 +109,23 @@ type expirer interface {
expire(now time.Time) (next *time.Duration)
// Get and set the expiration time.
expiry() time.Time
setExpiry(time.Time)
expiration() time.Time
setExpiration(time.Time)
}
// After mimics [time.After]; it waits for the given duration to elapse on the
// fakeClock, then sends the current time on the returned channel.
func (fc *fakeClock) After(d time.Duration) <-chan time.Time {
func (fc *FakeClock) After(d time.Duration) <-chan time.Time {
return fc.NewTimer(d).Chan()
}
// Sleep blocks until the given duration has passed on the fakeClock.
func (fc *fakeClock) Sleep(d time.Duration) {
func (fc *FakeClock) Sleep(d time.Duration) {
<-fc.After(d)
}
// Now returns the current time of the fakeClock
func (fc *fakeClock) Now() time.Time {
func (fc *FakeClock) Now() time.Time {
fc.l.RLock()
defer fc.l.RUnlock()
return fc.time
@@ -135,61 +133,73 @@ func (fc *fakeClock) Now() time.Time {
// Since returns the duration that has passed since the given time on the
// fakeClock.
func (fc *fakeClock) Since(t time.Time) time.Duration {
func (fc *FakeClock) Since(t time.Time) time.Duration {
return fc.Now().Sub(t)
}
// Until returns the duration that has to pass from the given time on the fakeClock
// to reach the given time.
func (fc *FakeClock) Until(t time.Time) time.Duration {
return t.Sub(fc.Now())
}
// NewTicker returns a Ticker that will expire only after calls to
// fakeClock.Advance() have moved the clock past the given duration.
func (fc *fakeClock) NewTicker(d time.Duration) Ticker {
var ft *fakeTicker
ft = &fakeTicker{
firer: newFirer(),
d: d,
reset: func(d time.Duration) { fc.set(ft, d) },
stop: func() { fc.stop(ft) },
// FakeClock.Advance() have moved the clock past the given duration.
//
// The duration d must be greater than zero; if not, NewTicker will panic.
func (fc *FakeClock) NewTicker(d time.Duration) Ticker {
// Maintain parity with
// https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/time/tick.go;l=23-25
if d <= 0 {
panic(errors.New("non-positive interval for NewTicker"))
}
fc.set(ft, d)
ft := newFakeTicker(fc, d)
fc.l.Lock()
defer fc.l.Unlock()
fc.setExpirer(ft, d)
return ft
}
// NewTimer returns a Timer that will fire only after calls to
// fakeClock.Advance() have moved the clock past the given duration.
func (fc *fakeClock) NewTimer(d time.Duration) Timer {
return fc.newTimer(d, nil)
func (fc *FakeClock) NewTimer(d time.Duration) Timer {
t, _ := fc.newTimer(d, nil)
return t
}
// AfterFunc mimics [time.AfterFunc]; it returns a Timer that will invoke the
// given function only after calls to fakeClock.Advance() have moved the clock
// past the given duration.
func (fc *fakeClock) AfterFunc(d time.Duration, f func()) Timer {
return fc.newTimer(d, f)
func (fc *FakeClock) AfterFunc(d time.Duration, f func()) Timer {
t, _ := fc.newTimer(d, f)
return t
}
// newTimer returns a new timer, using an optional afterFunc.
func (fc *fakeClock) newTimer(d time.Duration, afterfunc func()) *fakeTimer {
var ft *fakeTimer
ft = &fakeTimer{
firer: newFirer(),
reset: func(d time.Duration) bool {
fc.l.Lock()
defer fc.l.Unlock()
// fc.l must be held across the calls to stopExpirer & setExpirer.
stopped := fc.stopExpirer(ft)
fc.setExpirer(ft, d)
return stopped
},
stop: func() bool { return fc.stop(ft) },
// newTimer returns a new timer using an optional afterFunc and the time that
// timer expires.
func (fc *FakeClock) newTimer(d time.Duration, afterfunc func()) (*fakeTimer, time.Time) {
ft := newFakeTimer(fc, afterfunc)
fc.l.Lock()
defer fc.l.Unlock()
fc.setExpirer(ft, d)
return ft, ft.expiration()
}
afterFunc: afterfunc,
}
fc.set(ft, d)
// newTimerAtTime is like newTimer, but uses a time instead of a duration.
//
// It is used to ensure FakeClock's lock is held constant through calling
// fc.After(t.Sub(fc.Now())). It should not be exposed externally.
func (fc *FakeClock) newTimerAtTime(t time.Time, afterfunc func()) *fakeTimer {
ft := newFakeTimer(fc, afterfunc)
fc.l.Lock()
defer fc.l.Unlock()
fc.setExpirer(ft, t.Sub(fc.time))
return ft
}
// Advance advances fakeClock to a new point in time, ensuring waiters and
// blockers are notified appropriately before returning.
func (fc *fakeClock) Advance(d time.Duration) {
func (fc *FakeClock) Advance(d time.Duration) {
fc.l.Lock()
defer fc.l.Unlock()
end := fc.time.Add(d)
@@ -198,38 +208,34 @@ func (fc *fakeClock) Advance(d time.Duration) {
//
// We don't iterate because the callback of the waiter might register a new
// waiter, so the list of waiters might change as we execute this.
for len(fc.waiters) > 0 && !end.Before(fc.waiters[0].expiry()) {
for len(fc.waiters) > 0 && !end.Before(fc.waiters[0].expiration()) {
w := fc.waiters[0]
fc.waiters = fc.waiters[1:]
// Use the waiter's expriation as the current time for this expiration.
now := w.expiry()
// Use the waiter's expiration as the current time for this expiration.
now := w.expiration()
fc.time = now
if d := w.expire(now); d != nil {
// Set the new exipration if needed.
// Set the new expiration if needed.
fc.setExpirer(w, *d)
}
}
fc.time = end
}
// BlockUntil blocks until the fakeClock has the given number of waiters.
// BlockUntil blocks until the FakeClock has the given number of waiters.
//
// Prefer BlockUntilContext, which offers context cancellation to prevent
// deadlock.
// Prefer BlockUntilContext in new code, which offers context cancellation to
// prevent deadlock.
//
// Deprecation warning: This function might be deprecated in later versions.
func (fc *fakeClock) BlockUntil(n int) {
b := fc.newBlocker(n)
if b == nil {
return
}
<-b.ch
// Deprecated: New code should prefer BlockUntilContext.
func (fc *FakeClock) BlockUntil(n int) {
fc.BlockUntilContext(context.TODO(), n)
}
// BlockUntilContext blocks until the fakeClock has the given number of waiters
// or the context is cancelled.
func (fc *fakeClock) BlockUntilContext(ctx context.Context, n int) error {
func (fc *FakeClock) BlockUntilContext(ctx context.Context, n int) error {
b := fc.newBlocker(n)
if b == nil {
return nil
@@ -243,7 +249,7 @@ func (fc *fakeClock) BlockUntilContext(ctx context.Context, n int) error {
}
}
func (fc *fakeClock) newBlocker(n int) *blocker {
func (fc *FakeClock) newBlocker(n int) *blocker {
fc.l.Lock()
defer fc.l.Unlock()
// Fast path: we already have >= n waiters.
@@ -260,7 +266,7 @@ func (fc *fakeClock) newBlocker(n int) *blocker {
}
// stop stops an expirer, returning true if the expirer was stopped.
func (fc *fakeClock) stop(e expirer) bool {
func (fc *FakeClock) stop(e expirer) bool {
fc.l.Lock()
defer fc.l.Unlock()
return fc.stopExpirer(e)
@@ -269,81 +275,45 @@ func (fc *fakeClock) stop(e expirer) bool {
// stopExpirer stops an expirer, returning true if the expirer was stopped.
//
// The caller must hold fc.l.
func (fc *fakeClock) stopExpirer(e expirer) bool {
for i, t := range fc.waiters {
if t == e {
// Remove element, maintaining order.
copy(fc.waiters[i:], fc.waiters[i+1:])
fc.waiters[len(fc.waiters)-1] = nil
fc.waiters = fc.waiters[:len(fc.waiters)-1]
return true
}
func (fc *FakeClock) stopExpirer(e expirer) bool {
idx := slices.Index(fc.waiters, e)
if idx == -1 {
return false
}
return false
}
// set sets an expirer to expire at a future point in time.
func (fc *fakeClock) set(e expirer, d time.Duration) {
fc.l.Lock()
defer fc.l.Unlock()
fc.setExpirer(e, d)
// Remove element, maintaining order, setting inaccessible elements to nil so
// they can be garbage collected.
copy(fc.waiters[idx:], fc.waiters[idx+1:])
fc.waiters[len(fc.waiters)-1] = nil
fc.waiters = fc.waiters[:len(fc.waiters)-1]
return true
}
// setExpirer sets an expirer to expire at a future point in time.
//
// The caller must hold fc.l.
func (fc *fakeClock) setExpirer(e expirer, d time.Duration) {
func (fc *FakeClock) setExpirer(e expirer, d time.Duration) {
if d.Nanoseconds() <= 0 {
// special case - trigger immediately, never reset.
// Special case for timers with duration <= 0: trigger immediately, never
// reset.
//
// TODO: Explain what cases this covers.
// Tickers never get here, they panic if d is < 0.
e.expire(fc.time)
return
}
// Add the expirer to the set of waiters and notify any blockers.
e.setExpiry(fc.time.Add(d))
e.setExpiration(fc.time.Add(d))
fc.waiters = append(fc.waiters, e)
sort.Slice(fc.waiters, func(i int, j int) bool {
return fc.waiters[i].expiry().Before(fc.waiters[j].expiry())
slices.SortFunc(fc.waiters, func(a, b expirer) int {
return a.expiration().Compare(b.expiration())
})
// Notify blockers of our new waiter.
var blocked []*blocker
// Notify blockers of our new waiter.
count := len(fc.waiters)
for _, b := range fc.blockers {
fc.blockers = slices.DeleteFunc(fc.blockers, func(b *blocker) bool {
if b.count <= count {
close(b.ch)
continue
return true
}
blocked = append(blocked, b)
}
fc.blockers = blocked
}
// firer is used by fakeTimer and fakeTicker used to help implement expirer.
type firer struct {
// The channel associated with the firer, used to send expriation times.
c chan time.Time
// The time when the firer expires. Only meaningful if the firer is currently
// one of a fakeClock's waiters.
exp time.Time
}
func newFirer() firer {
return firer{c: make(chan time.Time, 1)}
}
func (f *firer) Chan() <-chan time.Time {
return f.c
}
// expiry implements expirer.
func (f *firer) expiry() time.Time {
return f.exp
}
// setExpiry implements expirer.
func (f *firer) setExpiry(t time.Time) {
f.exp = t
return false
})
}