Bound NIP-RS retention and search indexing#1771
Open
tlongwell-block wants to merge 11 commits into
Open
Conversation
Persist compact ordering watermarks for read-state coordinates, hard-delete superseded payloads, purge safely dominated history, and restrict stored full-text vectors to product-searchable kinds. Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
added 10 commits
July 12, 2026 13:27
Move the full search-vector rewrite to an explicit maintenance script, index defensive mention cleanup, fail closed on anomalous replay ordering, and align migration tag matching with runtime. Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Move watermark advancement, stale replay rejection, legacy soft-delete purging, and post-commit mention fencing into database triggers so migration 0007 is safe during mixed-version relay rollouts. Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
30078,d=read-state:<32 lowercase hex>, exactt=read-state) by hard-deleting superseded payloads instead of retaining soft-deleted historysearch_tsvallowlist for kinds0,9,40002,45001, and45003Runtime behavior
For conforming NIP-RS coordinates, replacement remains serialized by the existing per-coordinate advisory lock. The database trigger advances/checks the durable watermark on every insert—including inserts from pre-PR relay binaries during a rolling deploy—while the application checks both the live event and watermark. An older tuple is rejected (lowest event ID wins equal timestamps), and superseded payloads plus defensive mention rows are physically removed. Legacy soft-delete and NIP-09 paths are also converted to physical deletion by a database trigger, while retaining only the compact ordering watermark.
Other kind-30078 application data and all other NIP-33 kinds keep the existing soft-delete replacement behavior.
channel_idremains outside the NIP-33 replacement key.Deployment behavior
With
BUZZ_AUTO_MIGRATEenabled, migration 0007 takesSHARE ROW EXCLUSIVEonevents, so reads remain available while event inserts, updates, and deletes wait for commit. It:parameterized_event_watermarksand the mention-cleanup index;Migrations 0007 and 0009 are checksum-frozen at their published contents. Additive migration 0009 installs the database triggers that enforce watermark advancement/stale rejection and physical NIP-RS deletion across old and new relay binaries, plus the live-event fence for post-commit mention indexing. Exact duplicate idempotence is enforced at the durable coordinate level: an incoming tuple exactly equal to the watermark is skipped by the insert trigger and reports zero affected rows whether its payload is live or already deleted, eliminating replay/deletion resurrection races without another lock edge. Event replacement and legacy soft-delete cleanup both use the same event-row-before-mentions lock order. Additive migration 0010 replaces only the watermark guard function to make exact equality a zero-row no-op. Databases that already recorded migrations 0001–0009 upgrade directly to 0010 without a sqlx checksum mismatch.
Rows deleted explicitly through NIP-09 and lacking a live head are not purged by 0007. The migration does not rewrite
search_tsvor rebuild its GIN index.Migration 0008 serializes an emptiness check with event writers. A fresh empty database switches to the positive FTS allowlist. A populated database records 0008 as applied without changing its existing denylist expression or rewriting heap/TOAST/GIN storage. Search behavior therefore intentionally differs between fresh and existing installations until maintenance is performed.
Optional maintenance to convert populated databases
The PR is boot-safe without this step: populated databases keep their existing search expression. To convert one to the positive allowlist, run
scripts/maintenance/nip_rs_search_allowlist.sqlseparately in a sized maintenance window. It takesACCESS EXCLUSIVE, so event reads and writes block; it rewrites the generatedsearch_tsvcolumn and rebuilds the GIN index. Confirm headroom for replacement heap/TOAST/index files plus WAL before running it.Migration 0007's in-place hard deletes do not by themselves return filesystem space to the volume. The allowlist maintenance script rewrites the generated column and rebuilds the GIN index, replacing those relations, but it needs peak headroom while old and new files coexist and release can be delayed by old snapshots. If cleanup is run without that rewrite—or measured residual bloat remains afterward—use a table repack or
VACUUM FULLin a separately sized maintenance window.Validation
just cicargo test -p buzz-dbcargo check -p buzz-dbA → old writer B → NIP-09 delete B → replay B and X (A < X < B)regression proving the database advances to B, physically removes old payloads, fences stale post-commit mentions without deadlock, and rejects both exact and intermediate replayttagbin/just ci, focused recorded-0001–0009→0010 upgrade/concurrency regression, and Hermit-activated push hooks green atce10dd1417fcb22430f0e26b9db8d49d911fc42d; GitHub CI pending