Bucket received frames by their mac receive stamp and read one bucket back

This commit is contained in:
flamingcow
2026-08-04 20:04:30 -07:00
parent 8db5c29882
commit 703039587b
4 changed files with 185 additions and 47 deletions
+73 -26
View File
@@ -5,40 +5,87 @@ import (
"time"
)
func TestRateWindowLatestNeedsTwoBuckets(t *testing.T) {
w := newRateWindow(4)
if got := w.latest(rxRatePPS); got != 0 {
t.Errorf("empty ring gave %v, want 0", got)
// A frame lands in the bucket its receive stamp falls in, whenever the worker
// got round to draining it.
func TestRxObserveBucketsByStamp(t *testing.T) {
var s rxStats
s.observe(3*int64(time.Millisecond), 100)
s.observe(5*int64(time.Millisecond), 200)
s.observe(rateBucketNs+int64(time.Millisecond), 300)
if f, b := s.bucket(0); f != 2 || b != 300 {
t.Errorf("epoch 0 = %d frames, %d bytes; want 2, 300", f, b)
}
w.push(counterSet{t: time.Now(), s: sample{rxFrames: 100}})
if got := w.latest(rxRatePPS); got != 0 {
t.Errorf("one bucket gave %v, want 0", got)
if f, b := s.bucket(1); f != 1 || b != 300 {
t.Errorf("epoch 1 = %d frames, %d bytes; want 1, 300", f, b)
}
if got := s.newest.Load(); got != 1 {
t.Errorf("newest = %d, want 1", got)
}
}
// The newest pair alone, so a step in the rate shows at once instead of being
// averaged against everything still in the ring.
func TestRateWindowLatestUsesNewestPair(t *testing.T) {
w := newRateWindow(4)
t0 := time.Now()
w.push(counterSet{t: t0, s: sample{rxFrames: 100}})
w.push(counterSet{t: t0.Add(time.Second), s: sample{rxFrames: 300}})
if got := w.latest(rxRatePPS); got != 200 {
t.Errorf("rate = %v, want 200", got)
// A slot coming round again belongs to its new epoch, and the epoch it replaced
// reports nothing rather than the stale counts.
func TestRxBucketWraps(t *testing.T) {
var s rxStats
s.observe(1, 100)
s.observe(rateBuckets*rateBucketNs+1, 200)
if f, b := s.bucket(0); f != 0 || b != 0 {
t.Errorf("evicted epoch = %d frames, %d bytes; want 0, 0", f, b)
}
w.push(counterSet{t: t0.Add(2 * time.Second), s: sample{rxFrames: 400}})
if got := w.latest(rxRatePPS); got != 100 {
t.Errorf("rate = %v, want 100 rather than the mean of the ring", got)
if f, b := s.bucket(rateBuckets); f != 1 || b != 200 {
t.Errorf("new epoch = %d frames, %d bytes; want 1, 200", f, b)
}
}
func TestRateWindowLatestIgnoresZeroSpan(t *testing.T) {
w := newRateWindow(4)
t0 := time.Now()
w.push(counterSet{t: t0, s: sample{rxFrames: 100}})
w.push(counterSet{t: t0, s: sample{rxFrames: 300}})
if got := w.latest(rxRatePPS); got != 0 {
t.Errorf("rate = %v, want 0", got)
// The newest epoch may still be filling, so the rate comes from the one before.
func TestDirectionRateReadsOneBucketBack(t *testing.T) {
d := &direction{rxStats: []*rxStats{{}, {}}}
d.rxStats[0].observe(rateBucketNs+1, 500)
d.rxStats[1].observe(rateBucketNs+2, 700)
d.rxStats[0].observe(2*rateBucketNs+1, 900)
d.readRateBucket(time.Now())
if d.rateFrames != 2 || d.rateBytes != 1200 {
t.Errorf("rate bucket = %d frames, %d bytes; want the completed epoch, 2 and 1200",
d.rateFrames, d.rateBytes)
}
}
// A stamp only advances when a frame arrives, so an epoch that stops moving is
// a quiet wire and must not keep reporting the last bucket.
func TestDirectionRateGoesStale(t *testing.T) {
d := &direction{rxStats: []*rxStats{{}}}
d.rxStats[0].observe(rateBucketNs+1, 500)
d.rxStats[0].observe(2*rateBucketNs+1, 900)
now := time.Now()
d.readRateBucket(now)
if d.rateFrames == 0 {
t.Fatal("expected a rate while the epoch was still moving")
}
d.readRateBucket(now.Add(2 * rateStale))
if d.rateFrames != 0 || d.rateBytes != 0 {
t.Errorf("stale rate = %d frames, %d bytes; want 0, 0", d.rateFrames, d.rateBytes)
}
}
// Indexed oldest first, so the error window still spans the whole ring once it
// has wrapped.
func TestRateWindowIndexesOldestFirst(t *testing.T) {
w := newRateWindow(3)
for i := 1; i <= 5; i++ {
w.push(counterSet{s: sample{rxFrames: uint64(i)}})
}
if got := w.count(); got != 3 {
t.Fatalf("count = %d, want 3", got)
}
if got := w.at(0).s.rxFrames; got != 3 {
t.Errorf("oldest = %d, want 3", got)
}
if got := w.at(2).s.rxFrames; got != 5 {
t.Errorf("newest = %d, want 5", got)
}
}