Skip to content

Drop OR kind IS NULL from buildSelectQuery CTE — backfill is complete in production #27

Description

@MastaP

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions