Files
buzz/scripts/cutover/README.md
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d 666cfe4157 migrations: move 1321 cutover script out of the embedded sqlx dir
0002_backfill_default_community.sql is a psql operator script (\set, \ir,
:'var' interpolation) but lived in migrations/, which sqlx::migrate! embeds
wholesale. sqlx cannot run psql meta-commands, so every fresh deploy broke:
BUZZ_AUTO_MIGRATE=true exited the relay with 'syntax error at or near "\"',
buzz-admin migrate (the just setup path) failed identically with exit 5, and
both left a wedged half-migrated DB. This also reddened the buzz-db unit test
embedded_migrator_contains_consolidated_initial_schema (asserts exactly the
consolidated 0001), which CI never ran.

The module doc and that test already declare the intended invariant: the
embedded migrator is the consolidated 0001 only; cutover/backfill is a separate
operator script, not startup migration state. Restore it by relocating the
script to scripts/cutover/1321_backfill_default_community.sql (dropping the
0002_ prefix so it never reads as sqlx state again), switching its schema
include from \i to \ir so it resolves relative to the script rather than the
operator's cwd, and adding scripts/cutover/README.md as the operator runbook.

Wire the pure (infra-free) buzz-db tests into the unit path so this class of
regression can't hide again. CI installs nextest, so just test-unit takes the
nextest branch and bypasses run-tests.sh entirely — patch BOTH paths.

Verified locally on a pristine Postgres: buzz-admin migrate -> exit 0, one
migration row (v1); relay with BUZZ_AUTO_MIGRATE=true -> migrations complete,
readiness 200, 36 tables, no wedge. buzz-db --lib goes from RED (left:2 right:1)
to green. The relocated script's \ir resolves 0001 from a non-repo cwd.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-06-28 22:20:39 -04:00

3.7 KiB

1321 cutover: single-community → multi-tenant

1321_backfill_default_community.sql is a one-off operator script, not a startup migration. It takes a pre-1321 (single-community) Buzz Postgres to the 1321 multi-tenant schema, assigning every existing row to one default community derived from the deployment host.

It is not embedded in the relay binary. The relay's sqlx::migrate! embeds only migrations/0001_initial_schema.sql — the consolidated 1321 schema. Fresh deployments need only that; this script exists solely to carry existing pre-1321 data across the rewrite. (It uses psql client features — \set, \ir, :'var' interpolation, \gset — that the embedded sqlx migrator cannot run, which is why it must live here and not under migrations/.)

When you need it

Only when upgrading a Postgres that already holds pre-1321 single-community data to 1321. A brand-new deployment does not run this — it provisions from migrations/0001_initial_schema.sql (or schema/schema.sql) directly.

Preconditions

  • The DB is pristine pre-1321: no communities table, no community_id columns, no leftover legacy schema. The script's guard refuses to run otherwise (already-migrated, partially-migrated, or failed-prior-run states all raise and abort — see the guard block at the top of the script).
  • Take a pg_dump / PVC snapshot first. There is no down-path. The snapshot is the rollback.

Run it

The whole script runs in one transaction (all-or-nothing). Pass the deployment host as :host; it is normalized exactly as the relay normalizes a community host (rtrim(lower(host), '.')).

psql "$DATABASE_URL" \
     -v host="'your-deployment-host.example.com'" \
     -v ON_ERROR_STOP=1 \
     -f scripts/cutover/1321_backfill_default_community.sql

-f (not stdin) matters: the script \ir-includes ../../migrations/0001_initial_schema.sql relative to its own location, so it resolves the canonical 1321 schema regardless of your cwd.

What it does

  1. Renames all pre-1321 tables and enum types aside into a legacy schema.
  2. \ir-includes the repo's 0001 verbatim → the correct 1321 schema in a clean public (structurally identical to a fresh 1321 DB by construction — no hand-transcription).
  3. Creates the one default community from :host.
  4. Copies every row forward, stamped with that community_id (search_tsv regenerates as GENERATED; new-in-1321 columns default NULL).
  5. DROP SCHEMA legacy CASCADE.

After it commits

Boot the 1321 relay with BUZZ_AUTO_MIGRATE=false. The schema is already correct; the relay's idempotent boot-time ensure_configured_community / allowlist→relay_members backfill finds this community and no-ops.

If you want the relay's sqlx migrator to consider 0001 already applied (so a later BUZZ_AUTO_MIGRATE=true boot doesn't try to re-run it), seed its _sqlx_migrations row to match a fresh 1321 install:

-- version 1, description 'initial schema'; checksum must match the embedded 0001.
-- Easiest: diff _sqlx_migrations against a freshly-migrated 1321 DB and copy the row.

This is not required for correct operation — a fresh 0001 run against the already-correct schema is a no-op only if the migrator's checksum matches, so prefer leaving BUZZ_AUTO_MIGRATE=false unless you have a reason to seed.

Verify (post-commit, outside the txn)

SELECT count(*) FROM communities;                       -- expect 1
SELECT count(*) FROM events WHERE community_id IS NULL;  -- expect 0
SELECT to_regnamespace('legacy');                        -- expect NULL (gone)

For a stronger guarantee, take a constraint/trigger/index/column diff of this DB against a freshly-migrated 1321 DB — it should be empty.