beam: token_bucket blocking cell measures from the refill origin

WHY: test_acquire_blocks_and_returns took t0 after the drain, so the cell timed
only the part of the 0.05 s refill that happened after the scheduler returned to
it. Observed red in a loaded full-suite run at 7c36185:
'assert 0.03909331373870373 >= 0.04' -- 10.9 ms of untimed prefix. Deterministic
repro: pre-fix cell + 11 ms injected gap -> 0.03897/0.03912, rc 1.

FIX: t0 now precedes try_acquire(1.0), which is where the bucket sets _last, so
dt is >= 0.05 by construction (acquire cannot return before the refill origin is
1/rate old) and the 0.04 floor has ~10 ms of margin instead of 1 ms.

MEASURED: repo cell 9 passed; pre control rc 1 dt 0.03897-0.03912; post control
rc 0; same-gap in-process dt pre 0.039119 / post 0.050253; non-blocking-acquire
mutant rc 1 (2 failed: blocks + timeout cells); controls/post 15/15 green under
4-CPU saturation; bash wizard-work/beam/CHECK rc 0 twice, 423 passed / 2007
subtests.

CHECK: python3 /root/.hermes/wizard-outputs/volt/2026-10-28-q122/verify.py
This commit is contained in:
ledger committed 2026-10-28 14:14:46 +00:00
1 parent 7c36185c84
commit 960469db33
1 file changed
+10 -2
+10 -2
View File
@@ -21,12 +21,20 @@ def test_capacity_cap():
def test_acquire_blocks_and_returns(): def test_acquire_blocks_and_returns():
b = TokenBucket(rate=20.0, capacity=1.0) b = TokenBucket(rate=20.0, capacity=1.0)
b.try_acquire(1.0) # drain # t0 must precede the drain: try_acquire() debits the last token and sets
# the refill origin (self._last) inside the call, so a t0 taken after it
# silently subtracts whatever the scheduler spent in between. Observed at
# 7c36185 as `assert 0.03909331373870373 >= 0.04` in a loaded full-suite
# run: ~10.9 ms of scheduler gap, i.e. 0.05 s of withheld tokens minus a
# prefix the cell never measured. dt from before the drain is >= 0.05 by
# construction (acquire cannot return until the refill origin is 0.05 s
# old), so the 0.04 floor has margin instead of sitting 1 ms from the edge.
t0 = time.monotonic() t0 = time.monotonic()
b.try_acquire(1.0) # drain; sets the refill origin
ok = b.acquire(1.0, timeout=0.2) ok = b.acquire(1.0, timeout=0.2)
dt = time.monotonic() - t0 dt = time.monotonic() - t0
assert ok is True assert ok is True
assert dt >= 0.04 # had to wait ~0.05s for 1 token at 20/s assert dt >= 0.04 # 1 token at 20/s = 0.05s withheld, measured from the origin
def test_n_exceeds_capacity_raises(): def test_n_exceeds_capacity_raises():