CI / test (push) Successful in 9m30s
Each router's per-counter request and token counts, this minute's and the last, ride the standby's two-second exchange with the leader. The leader answers each standby with everyone else's sum, its own included, and puts the standbys' sum in force on its own router; each limiter adds the others' counts to its sliding window. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
72 lines
2.6 KiB
Go
72 lines
2.6 KiB
Go
package accounting
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Two control planes each allowing a key six of its ten a minute: once each
|
|
// has heard the other's count, neither allows another.
|
|
func TestALimitIsTheClustersOnceTheCountsAreShared(t *testing.T) {
|
|
now := time.Date(2026, 11, 1, 12, 0, 10, 0, time.UTC)
|
|
a, b := NewLimiter(), NewLimiter()
|
|
a.now, b.now = func() time.Time { return now }, func() time.Time { return now }
|
|
for range 6 {
|
|
if !a.Allow("k", 10, 1) || !b.Allow("k", 10, 1) {
|
|
t.Fatal("refused within the limit")
|
|
}
|
|
}
|
|
a.SetOthers(b.Counts())
|
|
b.SetOthers(a.Counts())
|
|
if a.Allow("k", 10, 1) || b.Allow("k", 10, 1) {
|
|
t.Error("twelve of ten allowed once both knew")
|
|
}
|
|
// Reporting the same counts again changes nothing: they are absolute.
|
|
a.SetOthers(b.Counts())
|
|
if got := a.Rate("k"); got != 12 {
|
|
t.Errorf("rate %v after the same counts twice", got)
|
|
}
|
|
// What the others counted is never charged back to them.
|
|
if c := a.Counts(); c.Current["k"] != 6 {
|
|
t.Errorf("own count %d", c.Current["k"])
|
|
}
|
|
}
|
|
|
|
// A key only the others have used is limited here from its first request,
|
|
// and their counts move into the previous minute with the clock.
|
|
func TestOthersCountsFollowTheMinute(t *testing.T) {
|
|
now := time.Date(2026, 11, 1, 12, 0, 50, 0, time.UTC)
|
|
a, b := NewLimiter(), NewLimiter()
|
|
a.now, b.now = func() time.Time { return now }, func() time.Time { return now }
|
|
for range 10 {
|
|
b.Allow("k", 10, 1)
|
|
}
|
|
a.SetOthers(b.Counts())
|
|
if a.Allow("k", 10, 1) {
|
|
t.Error("a key the cluster has used up was allowed where it had not been seen")
|
|
}
|
|
now = now.Add(20 * time.Second) // 12:01:10, a sixth into the next minute
|
|
if got := a.Rate("k"); got < 8 || got > 8.5 {
|
|
t.Errorf("rate %v: the others' last minute should weigh five sixths", got)
|
|
}
|
|
// Counts of a minute ago, sent late, land in the previous minute.
|
|
old := b.Counts()
|
|
old.Minute = now.Truncate(time.Minute).Add(-time.Minute)
|
|
old.Current, old.Previous = map[string]int64{"k": 3}, nil
|
|
a.SetOthers(old)
|
|
if got := a.Rate("k"); got < 2 || got > 3 {
|
|
t.Errorf("rate %v from late counts", got)
|
|
}
|
|
}
|
|
|
|
func TestRatesAddAlignsMinutes(t *testing.T) {
|
|
m := time.Date(2026, 11, 1, 12, 1, 0, 0, time.UTC)
|
|
sum := NewRates(m)
|
|
sum.Add(Rates{Requests: Counts{Minute: m, Current: map[string]int64{"k": 2}, Previous: map[string]int64{"k": 1}}})
|
|
sum.Add(Rates{Requests: Counts{Minute: m.Add(-time.Minute), Current: map[string]int64{"k": 5}}})
|
|
sum.Add(Rates{Requests: Counts{Minute: m.Add(-2 * time.Minute), Current: map[string]int64{"k": 100}}})
|
|
if sum.Requests.Current["k"] != 2 || sum.Requests.Previous["k"] != 6 {
|
|
t.Errorf("sum %+v", sum.Requests)
|
|
}
|
|
}
|