mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
feat(lane0): fold review round 2 — FTS column, drop stale migrations
Closes the last Lane-0 schema items before the frozen base:
- events.search_tsv TSVECTOR GENERATED ALWAYS AS to_tsvector('simple',
content) STORED + GIN idx_events_search_tsv. The Typesense->Postgres FTS
data shape, landed in Lane 0 because it touches the just-locked events
table (Quinn option A). GENERATED ALWAYS = single source of truth: proven
against PG that a client cannot forge search_tsv out of sync with content
(generated_always rejection). Index left minimal single-column GIN; the
search lane picks the final spelling after EXPLAIN (Max's caveat).
- Delete stale 0002_backfill_d_tag.sql / 0003_event_reminders.sql. In the
consolidated-from-scratch model 0001 already carries d_tag, not_before,
delivered_at, and idx_events_not_before; re-running the old additive
migrations would error (duplicate column / duplicate index name).
audit_log DDL shape confirmed for the audit-crate collapse (Dawn's lane):
PRIMARY KEY (community_id, seq), UNIQUE (community_id, hash), community_id
NOT NULL on every row. 0001 is the single source; buzz-audit drops its own
schema.rs / AUDIT_SCHEMA_SQL / ensure_schema() in the audit lane.
Re-proven against real Postgres — full fence suite green: T1 re-tenant
rejected, T6 cross-community member FK rejected, T6b same-community ok, T7
same channel UUID in two communities allowed, T8 host case-collision
rejected, T9 same event id in two communities allowed, plus the FTS
generated+GIN match and the forge-rejection. buzz-core: 189 + 2 doctests.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
co-authored by
Tyler Longwell
parent
9c0bbafa9b
commit
e349d76498
@@ -195,6 +195,15 @@ CREATE TABLE events (
|
||||
kind INT NOT NULL,
|
||||
tags JSONB NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
-- Full-text search vector (Typesense → Postgres FTS). Generated/STORED so
|
||||
-- it is a single source of truth — no sidecar indexer to keep coherent
|
||||
-- (Quinn option A, Lane-0 call). 'simple' config = no stemming/stopwords,
|
||||
-- matching the existing substring-ish search semantics; the search lane can
|
||||
-- revisit the config behind evidence. Tenant scoping is by the
|
||||
-- community-leading btree filters BitmapAnd-ed with the GIN probe, so the
|
||||
-- GIN index itself stays the minimal `GIN (search_tsv)` (Max's caveat:
|
||||
-- avoid btree_gin unless EXPLAIN proves it buys something).
|
||||
search_tsv TSVECTOR GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
|
||||
sig BYTEA NOT NULL,
|
||||
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
channel_id UUID,
|
||||
@@ -242,6 +251,11 @@ CREATE INDEX idx_events_parameterized
|
||||
WHERE d_tag IS NOT NULL AND deleted_at IS NULL;
|
||||
CREATE INDEX idx_events_not_before ON events (community_id, not_before)
|
||||
WHERE not_before IS NOT NULL AND deleted_at IS NULL AND delivered_at IS NULL;
|
||||
-- Full-text search. Minimal GIN over the generated tsvector; community scoping
|
||||
-- is supplied by the community-leading btree filters above (BitmapAnd), so this
|
||||
-- stays a single-column GIN. The search lane confirms the final spelling with
|
||||
-- EXPLAIN before its work lands (Quinn option A; Max's index-spelling caveat).
|
||||
CREATE INDEX idx_events_search_tsv ON events USING GIN (search_tsv);
|
||||
|
||||
-- ── Event mentions ────────────────────────────────────────────────────────────
|
||||
-- Conformance: "Channel-less global events and DMs" (#p fan-out). The join to
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
-- Backfill d_tag for existing NIP-33 range events (kind 30000–39999).
|
||||
-- Idempotent: only updates rows where d_tag is still NULL.
|
||||
-- Includes soft-deleted rows so the column is fully populated.
|
||||
-- Run once after adding the d_tag column to the events table.
|
||||
--
|
||||
-- Managed by sqlx migrations.
|
||||
|
||||
UPDATE events
|
||||
SET d_tag = COALESCE(
|
||||
(SELECT elem->>1
|
||||
FROM jsonb_array_elements(tags) AS elem
|
||||
WHERE elem->>0 = 'd'
|
||||
LIMIT 1),
|
||||
''
|
||||
)
|
||||
WHERE kind BETWEEN 30000 AND 39999
|
||||
AND d_tag IS NULL;
|
||||
@@ -1,17 +0,0 @@
|
||||
-- Add NIP-ER event-reminder columns and due-delivery index to the events table.
|
||||
--
|
||||
-- `not_before` is the reminder's scheduled delivery time (Unix seconds);
|
||||
-- `delivered_at` records when the scheduler published it. Both are nullable —
|
||||
-- non-reminder events leave them NULL. The partial index covers only
|
||||
-- undelivered, live reminders so the scheduler's due-query stays cheap.
|
||||
--
|
||||
-- `events` is partitioned by RANGE (created_at); ALTER TABLE on the parent
|
||||
-- cascades the columns to every partition, and CREATE INDEX on the parent
|
||||
-- builds a partitioned index that propagates to each partition.
|
||||
--
|
||||
-- Managed by sqlx migrations.
|
||||
|
||||
ALTER TABLE events ADD COLUMN not_before BIGINT;
|
||||
ALTER TABLE events ADD COLUMN delivered_at BIGINT;
|
||||
CREATE INDEX idx_events_not_before ON events (not_before)
|
||||
WHERE not_before IS NOT NULL AND deleted_at IS NULL AND delivered_at IS NULL;
|
||||
Reference in New Issue
Block a user