Summary
The active-expiry tick's 1ms budget covers only sweep 1 (whole-key probabilistic sampling). Sweep 2 — the hash-field TTL reap — then iterates every HashWithTtl key in the db and calls reap_expired_fields_one_hash on each, with no time budget, no sampling, and no batch cap (src/server/expiration.rs:135-143).
A db with many field-TTL'd hashes stalls the shard event loop for the full reap, every 100ms tick. The reap itself can be arbitrarily expensive per key (draining expired fields, possibly downgrading HashWithTtl → Hash or deleting the key).
Compounding it, the key list is collected by a full table scan each tick (hashes_with_field_expiry, src/storage/db/accessors.rs:408 — see #541).
Fix direction
Extend the sweep-1 pattern: carry the same Instant budget into sweep 2 and resume from a cursor next tick, or sample like sweep 1 does. Should ride the expiry-index work in #541 (the HashWithTtl set becomes an indexed population instead of a scan).
Evidence anchors
src/server/expiration.rs:130 (budget check confined to sweep 1's loop), :135-143 (unbounded sweep 2)
Summary
The active-expiry tick's 1ms budget covers only sweep 1 (whole-key probabilistic sampling). Sweep 2 — the hash-field TTL reap — then iterates every
HashWithTtlkey in the db and callsreap_expired_fields_one_hashon each, with no time budget, no sampling, and no batch cap (src/server/expiration.rs:135-143).A db with many field-TTL'd hashes stalls the shard event loop for the full reap, every 100ms tick. The reap itself can be arbitrarily expensive per key (draining expired fields, possibly downgrading
HashWithTtl→Hashor deleting the key).Compounding it, the key list is collected by a full table scan each tick (
hashes_with_field_expiry,src/storage/db/accessors.rs:408— see #541).Fix direction
Extend the sweep-1 pattern: carry the same
Instantbudget into sweep 2 and resume from a cursor next tick, or sample like sweep 1 does. Should ride the expiry-index work in #541 (theHashWithTtlset becomes an indexed population instead of a scan).Evidence anchors
src/server/expiration.rs:130(budget check confined to sweep 1's loop),:135-143(unbounded sweep 2)