Background
PR #24 added kind to event_tags and pushed it into the materialized CTE in buildSelectQuery to avoid hash-joining ~95k membership-event tag rows. The CTE shape is currently:
WITH _tag_ids AS MATERIALIZED (
SELECT event_id FROM event_tags
WHERE key = $1 AND value IN (...)
AND (kind IN (...) OR kind IS NULL)
)
The OR kind IS NULL was a backwards-compat escape hatch for the rollout window: rows inserted before the column existed (or by the OLD relay during the brief overlap between manual prep and the new image going live) had NULL kind. The OR kept those rows visible to reads while a backfill UPDATE filled them in.
Current state
Production has zero NULL-kind rows after a re-backfill on 2026-05-09 (the 37 stragglers from the 8-min cutover overlap). All inserts since the new image went live carry a non-NULL kind.
The OR clause is now strict dead code in production.
Why drop it
Verified in production with EXPLAIN (ANALYZE, BUFFERS):
|
With OR kind IS NULL |
Without |
| Wall clock |
13.5 ms |
11.8 ms |
| Plan |
BitmapOr + Bitmap Heap Scan |
Index Only Scan |
| Heap fetches |
many (via heap scan) |
2 (~zero) |
| Shared buffer hits |
8,375 |
7,225 |
The OR kind IS NULL forces the planner into a BitmapOr + Bitmap Heap Scan because IS NULL needs a separate predicate evaluation. Dropping it lets the planner use a single tight Index Only Scan on the existing covering index (key, value, kind, event_id).
Change
Single-line edit in buildSelectQuery:
if len(kindInts) > 0 {
subQ = subQ.Where(squirrel.Eq{"kind": kindInts}) // drop the squirrel.Or wrapper
}
And remove the corresponding test fixtures / assertions that simulated NULL-kind rows for backwards compat — they're vestigial now.
Risks
- If a future migration somehow lands NULL
kind rows (e.g. a future ALTER TABLE ADD COLUMN for another tag attribute that doesn't backfill atomically), they would silently fall out of reads. Mitigation: hold a one-off SELECT COUNT WHERE kind IS NULL in pre-deploy verification, or add a NOT NULL constraint on the column.
- Adding the NOT NULL constraint on a populated table is a rewrite — but the column is already populated, so it can be done as a CHECK constraint NOT VALID + VALIDATE, or accepted as a one-time table rewrite during a maintenance window.
Either of those tightening steps is itself follow-up. The immediate change just drops the OR.
Background
PR #24 added
kindtoevent_tagsand pushed it into the materialized CTE inbuildSelectQueryto avoid hash-joining ~95k membership-event tag rows. The CTE shape is currently:The
OR kind IS NULLwas a backwards-compat escape hatch for the rollout window: rows inserted before the column existed (or by the OLD relay during the brief overlap between manual prep and the new image going live) had NULLkind. The OR kept those rows visible to reads while a backfill UPDATE filled them in.Current state
Production has zero NULL-kind rows after a re-backfill on 2026-05-09 (the 37 stragglers from the 8-min cutover overlap). All inserts since the new image went live carry a non-NULL
kind.The OR clause is now strict dead code in production.
Why drop it
Verified in production with EXPLAIN (ANALYZE, BUFFERS):
OR kind IS NULLThe
OR kind IS NULLforces the planner into a BitmapOr + Bitmap Heap Scan because IS NULL needs a separate predicate evaluation. Dropping it lets the planner use a single tight Index Only Scan on the existing covering index(key, value, kind, event_id).Change
Single-line edit in
buildSelectQuery:And remove the corresponding test fixtures / assertions that simulated NULL-kind rows for backwards compat — they're vestigial now.
Risks
kindrows (e.g. a futureALTER TABLE ADD COLUMNfor another tag attribute that doesn't backfill atomically), they would silently fall out of reads. Mitigation: hold a one-off SELECT COUNT WHERE kind IS NULL in pre-deploy verification, or add a NOT NULL constraint on the column.Either of those tightening steps is itself follow-up. The immediate change just drops the OR.