mirror of
https://github.com/block/buzz.git
synced 2026-10-31 06:21:34 +00:00
The NIP-ER reminder scheduler published the reminder event to Redis first and only then claimed it (`claim_due_reminder`), treating a duplicate publish as harmless because subscribers dedup by event id. That is the old unsafe ordering: across N pods a due reminder could be published more than once, violating the claim-before-side-effect rule for periodic producers (every side effect must be claimed exactly once, not deduped after the fact). Rewire to claim-before-publish using the stamp-guarded primitives that were already built and wired into buzz-db but had zero callers: `claim_due_reminder_with_stamp` (event.rs:1186) and `release_due_reminder` (event.rs:1213). Each attempt mints a unique per-pod stamp; the scheduler claims first, publishes only on a winning claim (`Ok(true)`) and `continue`s on the loser (`Ok(false)`) so the loser never produces the side effect, and releases its own claim on publish failure via compare-and-clear so the reminder is redeliverable next tick. `events.delivered_at` is only ever read as a NULL/non-NULL sentinel (due-reminder query guard + partial index), never as a wall-clock value, so an opaque stamp is safe to store there. The unused convenience wrapper `claim_due_reminder` (seconds stamp) is left in place as public API; the scheduler no longer uses it. Tests (buzz-db, Postgres-backed, verified locally against the dev DB): - claim_due_reminder_is_won_by_exactly_one_of_two_racing_pods: two pods, two stamps, one reminder -> exactly one wins; the single winning claim is the proof of exactly one publish side effect. - release_due_reminder_rolls_back_only_the_matching_stamp: a wrong-stamp release is a no-op (cannot clear another pod's claim); a matching-stamp release makes the reminder reclaimable for retry. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>