NIP-52 republish uses non-monotonic created_at (live ticket counts can stall) #26
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Backend counterpart of the webapp fix aiolabs/webapp#122.
Problem
nostr_publisher.build_nip52_event()stampscreated_at=int(time.time())(nostr_publisher.py:115). NIP-52 calendar events (kinds 31922/31923) are addressable / replaceable, and relays only push a replacement to open subscriptions when itscreated_atis strictly newer than the version they already hold.created_atis second-resolution. When the event is republished after a ticket sells (set_ticket_paid→ decrementamount_tickets/ incrementsold→ republish), two republishes landing in the same wall-clock second produce equalcreated_at. The relay treats the second as not-newer and silently drops it for live subscribers — so a connected client's "X tickets remaining" badge doesn't update until a reload / fresh REQ. This is the same root cause we hit while debugging the webapp's live ticket count.Proposed fix
We already persist the last published timestamp on the model:
Event.nostr_event_created_at(models.py:76), set after each publish innostr_hooks.py:45. Use it as a monotonic anchor:Extract a small pure helper (mirrors the webapp's
monotonicCreatedAt/docs/nostr-patterns/replaceable-events.md) and use it inbuild_nip52_event. The kind-5 delete event (build_nip52_delete_event) is not replaceable, so it keeps plainint(time.time()).Known limitation (out of scope)
Two concurrent sales that read the same stored
nostr_event_created_atbefore either writes back can still compute the same+1. Fully closing that needs a row-level lock / transaction around read-bump-publish-persist. The stored-anchor approach already fixes the dominant case (sequential republishes within the same second) and is strictly better thantime.time(); the concurrency hardening can be a follow-up.Testing
Unit-test the helper (no-prior, same-second bump, wall-clock tracking, future-dated prior, strictly-increasing burst), matching the webapp's
timestamp.spec.ts.