Take the rate epoch from the slowest stream's frontier, since reading a bucket claims every stream has delivered through it

This commit is contained in:
flamingcow
2026-08-04 22:38:10 -07:00
parent 7b2601a02e
commit 49eaa8a8a8
2 changed files with 55 additions and 8 deletions
+44 -1
View File
@@ -43,7 +43,9 @@ func TestRxBucketWraps(t *testing.T) {
}
}
// The newest epoch may still be filling, so the rate comes from the one before.
// Every stream's frontier is past the epoch being read, so the bucket is
// complete across the board and the rate comes from the one before the lowest
// frontier.
func TestDirectionRateReadsOneBucketBack(t *testing.T) {
d := &direction{rxStats: []*rxStats{{}, {}}}
r0 := rateRun{stats: d.rxStats[0]}
@@ -51,6 +53,7 @@ func TestDirectionRateReadsOneBucketBack(t *testing.T) {
r0.add(rateBucketNs+1, 500)
r1.add(rateBucketNs+2, 700)
r0.add(2*rateBucketNs+1, 900)
r1.add(2*rateBucketNs+2, 1100)
r0.flush()
r1.flush()
@@ -61,6 +64,46 @@ func TestDirectionRateReadsOneBucketBack(t *testing.T) {
}
}
// Reading a bucket is a claim that every stream has delivered through it, so a
// stream still short of the epoch holds the read back rather than having its
// half-filled bucket summed.
func TestDirectionRateWaitsForSlowestStream(t *testing.T) {
d := &direction{rxStats: []*rxStats{{}, {}}}
r0 := rateRun{stats: d.rxStats[0]}
r1 := rateRun{stats: d.rxStats[1]}
r0.add(rateBucketNs+1, 500)
r0.add(2*rateBucketNs+1, 900)
r0.flush()
// One stream has never delivered at all, so there is no epoch every stream
// has reached and nothing to read.
d.readRateBucket(time.Now())
if d.rateFrames != 0 || d.rateBytes != 0 {
t.Errorf("rate = %d frames, %d bytes; want nothing while a stream has no frontier",
d.rateFrames, d.rateBytes)
}
// The straggler is still filling the epoch the leader finished, so its
// bucket must not be read yet.
r1.add(rateBucketNs+2, 700)
r1.flush()
d.readRateBucket(time.Now())
if d.rateFrames != 0 || d.rateBytes != 0 {
t.Errorf("rate = %d frames, %d bytes; want nothing while a stream is still filling the epoch",
d.rateFrames, d.rateBytes)
}
// Once it moves past, the bucket is complete for both streams and is read
// whole.
r1.add(2*rateBucketNs+2, 1100)
r1.flush()
d.readRateBucket(time.Now())
if d.rateFrames != 2 || d.rateBytes != 1200 {
t.Errorf("rate = %d frames, %d bytes; want the whole 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) {