Repository navigation
BUZZ-413: Fence shared event database writes #8658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b70b41c
1976583
4877960
0fe7f9c
a05c3c4
0c3ebcb
eb2c2c8
f1570c4
4688052
21f8dc6
b76879c
4aef33b
572e30e
3564dc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -477,6 +477,7 @@ async fn insert_event_on( | |
| return Err(DbError::EphemeralEventRejected(kind_u16)); | ||
| } | ||
|
|
||
| tx.guard_event_author(event).await?; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 IMPORTANT — identity locks taken after domain row locks. The ordering rules in Two ways to fix it: run the guard before any domain row lock (at admission, or at the top of each caller), or rewrite the ordering rules to match what callers actually do and state the worker-side requirement that follows.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 Addressed using the second option in 21f8dc6, included in head 572e30e. The documentation now describes the actual call order: domain locks may precede the guard, and later relay-signed inserts can acquire another global identity lock after the first scoped lock. Per-call sorting is explicitly not a transaction-wide guarantee. The future exclusive fencing transaction must write only restriction/request/checkpoint state while holding identity locks. It must not lock or mutate serving-domain rows; cleanup must use later transactions. Complete exclusive identity sets must also be audited against multi-signer transactions before enabling the worker. Added production artifact Move concurrency regressions for a fence winning first and a Move waiting for the relay identity lock while a user fence queues. The tested interleavings complete with the expected persisted/rolled-back state and zero recorded database deadlocks. The channel-TTL race also passes. These tests do not establish universal deadlock safety; the real deletion worker remains disabled. |
||
| let id_bytes = event.id.as_bytes(); | ||
| let pubkey_bytes = event.pubkey.to_bytes(); | ||
| let sig_bytes = event.sig.serialize(); | ||
|
|
@@ -1880,6 +1881,7 @@ pub(crate) async fn insert_event_with_thread_metadata_tx( | |
| return Err(DbError::EphemeralEventRejected(kind_u16)); | ||
| } | ||
|
|
||
| tx.guard_event_author(event).await?; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 IMPORTANT — archive/unarchive takes effect despite the refusal. For kinds 9035/9036, Fix: apply the archive change and insert the request in one transaction that runs this guard, and publish after commit. A separate check before the handler would still race a restriction being installed. Please also add archive/unarchive to the HTTP/WS before-and-after-restriction tests, with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 Fixed in 4688052, included in head 572e30e. Archive/unarchive validation now produces a plan without changing state. The guarded request insert, archived_identities mutation, and relay delta persist in one transaction; notification dispatch follows commit. A rejected request or failed delta insert rolls all three back. Duplicate requests do not repeat the mutation. Added HTTP/WebSocket before-and-after-fence regressions, unchanged archive/snapshot assertions, delta-failure rollback, and fence commit/rollback races. These passed in the merged tree's PostgreSQL suite (1,189 tests). Remaining boundary: the snapshot refresh is post-commit. Its failure propagates, and a duplicate retry refreshes it without repeating the archive mutation. There is no durable background refresh queue, so a crash after commit can leave the snapshot stale until a later refresh. This limitation is documented.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 Follow-up fixed in 3564dc2: replaying an already-processed archive/unarchive request now leaves an already-current snapshot unchanged, including its event ID and timestamp, and does not broadcast a replacement. The publisher compares the saved snapshot with canonical archive state and rechecks that state before taking the no-op path. Missing/stale snapshots are still repaired after a failed publication; the existing post-write drift protection remains. The new replay regression failed against the previous code, then passed with the fix. It covers repeated archive/unarchive requests and replay after a later opposite operation, asserting that all database rows remain unchanged. Recovery tests cover both missing and stale snapshots and a further retry after repair. A deterministic concurrency test changes canonical state while the publisher is paused before its no-op decision and verifies repair. Validation: all 6 PostgreSQL archive/fencing tests and 8 existing archive consent/publisher tests passed, as did the normal pre-push hooks. The PostgreSQL fixture exercises the HTTP route and authenticated WebSocket event handler; I did not rerun the separate live WebSocket AUTH simulation. The full repository just ci run was not restarted. |
||
| let id_bytes = event.id.as_bytes(); | ||
| let pubkey_bytes = event.pubkey.to_bytes(); | ||
| let sig_bytes = event.sig.serialize(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 IMPORTANT — archive requests and deltas are missing from
event_mentions. Before this change, the request was stored throughDb::insert_event_with_thread_metadataand the delta throughDb::insert_event. Both wrappers callinsert_mentions_in_transactionafter a new insert (event.rsL2289–2292 and L2829–2831). This function calls the low-levelinsert_event_in_transactionfor both events and never indexes theirptags. Single-#pqueries and counts use anINNER JOIN event_mentions(event.rsL628–641, L851–861; the relay pushes#pfilters down to this path inreq.rsL1220–1231). As a result, newly accepted 9035/9036 requests and their 8002/8003 deltas no longer show up in a#p: [target]subscription or history query, which is how a client replays one identity's archive history.Fix: call
crate::insert_mentions_in_transaction(&mut tx, event, None)for the request wheninserted, and for the delta when it's inserted, inside this transaction. Please add a regression test that queries#p= target through the production path and expects both events.