Repository navigation
Conversation
Lease expiry is a duration question, so it must be measured against real wall time (PR NodeDB-Lab#246 established this after the HlcClock::peek wedge). This extracts the clock behind a WallClock trait so the expiry check is injectable: RealWallClock for production, MockClock for tests. count_matching_leases now takes &dyn WallClock instead of calling super::wall_now_ns() directly. Adds 3 MockClock tests that pin the clock and prove expiry uses the injected wall clock, not the HLC. Follow-up to NodeDB-Lab#246; small, single-purpose per review feedback.
The clock abstraction is a general utility (lease expiry is just the first consumer), so it belongs in util/ next to bounded_json/bounded_msgpack. No behaviour change; imports updated. wall_now_ns re-export widened to pub(crate) so util can reach it.
Reverse-direction guard for the NodeDB-Lab#246 wedge: a lease that looks slightly expired may still be live on a holder whose clock is behind ours, so the drain filter now keeps leases inside a 5-minute MAX_SKEW window and drops only leases expired beyond it. Safety-first per NodeDB-Lab#246: never drop a live hold; a genuinely-dead lease drains up to MAX_SKEW later. - live_threshold = now.saturating_sub(MAX_SKEW_NS) in count_matching_leases - expired() fixture moved beyond the skew window (60s past is now live) - 3 new MockClock tests: within-window kept, beyond-window dropped, future kept
|
Closing: right problem, and it is a tracked one — but this constant converts the crash wedge into a guaranteed DDL failure, and the clamp cannot land before the crash-release hook it depends on. The premise holdsCross-node lease expiry really is unbounded here, and #165 already tracks it:
The constant is fatal
The skew window is 8.6× the drain timeout, and one full lease lifetime. A crashed holder's lease expires at T. On The Trade-off section says "a genuinely-dead lease drains up to Expiry is the only crash-recovery path today
This is why #165 lists the clamp and the SWIM-Dead release hook together. The hook has to land first. Once a Dead verdict releases leases as an event, expiry becomes a backstop and a skew window is affordable. Clamping expiry while it is still the only path removes crash recovery for the width of the window. Ordering, and the constraint on the constant:
Test fixture masking
AlsoStacked on #249, which is closed, so this carries the whole Filed separatelyThe dead |
Summary
Reverse-direction guard for the #246 wedge. The hotfix (
7168ecc55) judges expiry against the local wall clock, but a lease stamped by a holder whose clock is behind ours then looks already-expired → false expiry → DDL proceeds under a live holder (the correctness bug #246 refuses to trade for).count_matching_leasesnow keeps any lease whose expiry is inside a 5-minute MAX_SKEW window (expires_at > now - MAX_SKEW) and drops only leases expired beyond it.Stacked on #249 (WallClock trait): this branch contains the trait first; once #249 merges, this PR's diff reduces to the clamp + tests only.
Changes
MAX_SKEW_NS = 300_000_000_000(5 min) indrain_propose.rslive_threshold = now.saturating_sub(MAX_SKEW_NS)in the drain filterexpired()test fixture moved beyond the skew window (60s-past leases are now live by design)MockClocktests: within-window kept, beyond-window dropped, far-future keptTrade-off (safety-first, per #246)
A genuinely-dead lease drains up to
MAX_SKEWlater. Never dropping a live hold is the priority.Verification (solve-first)
cargo check -p nodedb --lib --tests— PASSED (exit 0)cargo test -p nodedb --lib lease::drain_propose::tests— 15/15 PASSED