Files
buzz/crates/buzz-test-client/tests
Luke Tornquist 88697f1c59 fix(relay): apply kind 5 / 9005 deletions atomically (#8944)
## Summary

Kind 5 and 9005 deletions now store the event and soft-delete their
targets in one transaction.

Before this PR the deletion committed first. The target soft-delete ran
afterwards as a best-effort side effect, and a failure there was only
logged. Resending the deletion then returned `duplicate:` without
retrying. A crash between the two commits had the same result. Either
way the relay accepted a deletion while its target stayed live.

The bridge `include_aux` closure still compensates for those rows. Its
second hop returns deletions of the reactions and edits it found, so
clients can cancel them. This PR keeps that hop, because rows already
torn this way can't be repaired by a resend:

- Ingest rejects events more than 900 seconds from server time.
- Clients don't resend deletions that were accepted.
- During a rolling deploy, old pods keep writing torn rows until they
are replaced.

Dropping the hop waits for a follow-up, once existing torn rows have had
a verified repair and no old writers remain.

### Changes

- **Atomic deletion.** Kind 9005, and kind 5 with `e` targets, go
through `Db::insert_deletion_event`, driven by
`side_effects::persist_deletion`. The write stores the event and its
mentions, soft-deletes the targets and updates their thread counters. It
follows the existing `persisted_atomically` path used for workflow
deletion and kinds 9000/9002.
- A failure stores nothing and rejects the event, so the client can
resend.
- A resend of a stored deletion inside the ingest window soft-deletes
any target still live and is dispatched as inserted. It doesn't repair
older rows.
- Live 39005, reaction-table cleanup and the 9005 system message still
run after the commit and stay best-effort.
  - Kind 5 deletions of `a` coordinates stay a side effect.
- **Closure unchanged.** The channel window and legacy thread-reply
paths keep both hops, and the docs (`docs/bridge-channel-window.md`,
`docs/nips/NIP-CW.md`) are unchanged.

### What got simpler

Deletion no longer has a window where the event is accepted but not
applied. `handle_standard_deletion_event` is now only the `a`-coordinate
path. Target planning and soft-delete for `e` targets live in one place,
`persist_deletion`.

### Follow-ups (not in this PR)

- Repair the existing torn rows: soft-delete the live targets of stored
kind 5 / 9005 `e` deletions and fix their thread counters. Then drop the
closure's deletion-of-aux hop.
- Hop 1 still probes every partition once per row id. A partition bound
or the indexed `reactions` table would cut that.

### Related issue

N/A, found while investigating relay database CPU.

### Testing

- `torn_deletions_still_cancel_live_targets_in_both_windows` (bridge
Postgres) stores kind 5 and 9005 deletions without soft-deleting their
reaction and edit targets. It queries the channel window and the legacy
thread path with `include_aux`, without a resend, and asserts each live
target comes back with its deletion.
-
`insert_deletion_event_target_failure_stores_nothing_and_resend_applies`
(buzz-db Postgres) uses a trigger to make the target's soft-delete fail.
It asserts nothing is stored and the target is unchanged, then that a
resend applies both.
- `insert_deletion_event_resend_repairs_a_live_target` (buzz-db
Postgres): a resend repairs a deletion stored without its target's
soft-delete, and a further resend changes nothing.
- `failed_target_soft_delete_stores_nothing` and
`resent_deletion_repairs_a_live_target` (relay Postgres) cover the same
two cases through ingest, for kinds 5 and 9005.
- `test_channel_window_rows_overlays_and_exact_multiple_exhaustion`
(`e2e_nostr_interop`, Relay E2E lane) deletes its reaction and asserts
the reaction leaves the closure.
- Blox: fmt, clippy (`buzz-db`, `buzz-relay`, `buzz-test-client`), and
the `buzz-db` and `buzz-relay` Postgres lanes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:38:26 -05:00
..