provider: a key that says it is out of its windows while they are being read has them read again once that reading is back (TestRestingKeyReadAgainInAnOrderedGroup red on macOS CI, runs 37467016755 and on main since 555f5706) — since 555f5706 the first planning of a lone key reads its windows behind the request. When that reading was still out as the key refused for quota, StaleKeyAllowance only zeroed the time it was read, and the reading, asked before the refusal, came back, set it to now and was trusted for a minute: the rest out of the key's windows wasn't lifted by the reading planned after it, though its limit had been raised. StaleKeyAllowance now marks a reading that is out (stale), and once it is back the key's windows are read again at once; forgetting every key's windows clears the mark. TestKeyAllowanceStaleMidRead (the first reading held until after StaleKeyAllowance, then answered; the second answered with the week half used) fails without the change (asked once) and passes -race -count=20 with the other TestKeyAllowance tests. The gateway test fails every time with its first reading delayed 300 ms ("still rests by window …, its windows read 2 times", as CI said) and passes with the change. go test -race ./internal/provider (8 of 9 runs pass; the first run's failure wasn't kept and didn't come back), go test -race -count=10 -run 'TestResting|Key|Lone|Allowance' ./internal/gateway, go vet, gofmt -l pass.

This commit is contained in:
yetone committed 2026-10-06 21:29:55 +08:00
1 parent 29f4909262
commit ee2588c268
2 files changed
+40 -2

No files matched your search

+9 -2
View File
@@ -31,6 +31,9 @@ type keyAllowance struct {
a Allowance
at time.Time // when read; zero to read again at the next ask
loading bool
// stale: made stale (StaleKeyAllowance) while a reading was out, which
// was asked before the key said it was out and is followed by another
stale bool
}
// keyAllowanceAge is how long a key's windows as read go unasked again;
@@ -124,6 +127,10 @@ func readKeyAllowance(p Provider, id string, gen int) {
}
e.loading, e.at = false, now
renewed := err == nil && e.keep(allowanceOf(ws, now), SpentShareOf(p.Routing), now)
if e.stale {
e.stale, e.loading, e.at = false, true, time.Time{}
go readKeyAllowance(p, id, gen)
}
c.Unlock()
if renewed {
tellRenewed("", id)
@@ -215,7 +222,7 @@ func StaleKeyAllowance(p Provider) {
c := &keyAllowances
c.Lock()
if e := c.m[keyAllowanceID(p)]; e != nil {
e.at = time.Time{}
e.at, e.stale = time.Time{}, e.loading
}
c.Unlock()
}
@@ -228,7 +235,7 @@ func forgetKeyAllowances() {
c := &keyAllowances
c.Lock()
for _, e := range c.m {
e.at, e.loading = time.Time{}, false
e.at, e.loading, e.stale = time.Time{}, false, false
}
c.gen++
c.Unlock()
+31
View File
@@ -348,6 +348,37 @@ func TestKeyAllowanceForgottenMidRead(t *testing.T) {
}
}
// A key that says it is out of its windows while a reading of them is
// out has them read again once that reading is back, without being asked:
// the reading was asked before, and the rest it would lift is planned on
// the one after (an ordered group's member read as it is planned).
func TestKeyAllowanceStaleMidRead(t *testing.T) {
keyLimitsHome(t)
week := time.Now().Add(72 * time.Hour).Truncate(time.Second)
release := make(chan struct{})
var first atomic.Bool
srv, asked := sub2apiServer(t, func() ([]byte, int) {
if first.CompareAndSwap(false, true) {
<-release
return weekUsed(t, "800", week), 200
}
return weekUsed(t, "400", week), 200
})
var once sync.Once
t.Cleanup(func() { once.Do(func() { close(release) }) })
p := sub2apiKey(srv)
KeyAllowance(p)
for asked.Load() < 1 {
time.Sleep(5 * time.Millisecond)
}
StaleKeyAllowance(p) // out of its week, as the request found
once.Do(func() { close(release) })
waitAllowance(t, p, func(a Allowance) bool { u, _ := a.For("gpt-6-astra", time.Now()); return u > 0 && u < 100 })
if n := asked.Load(); n != 2 {
t.Fatalf("asked %d times", n)
}
}
// Until the key is first read (magpie just started), its windows are the
// ones its card last showed, kept on disk; a window whose reset passed
// since is empty again.