Files
buzz/scripts/reconcile-schema-after-pgschema.sql
Luke Tornquist 0ccf934b88 fix(db): disable heartbeat vacuum truncation (#6898)
## Why
`replica_heartbeat` is updated continuously, but autovacuum heap
truncation can take an `ACCESS EXCLUSIVE` lock whose replay cancels
concurrent hot-standby reads. Truncating a fixed, single-row table
provides no meaningful space benefit.

## What
- Add migration 0034 to set `vacuum_truncate = false` on
`replica_heartbeat`
- Keep the desired-state schema aligned for fresh installations
- Extend migration coverage so both paths retain the reloption

## Risk Assessment
Low — this disables only autovacuum's heap-truncation phase for a
single-row table; normal vacuum cleanup remains enabled. The tradeoff is
retaining at most the table's otherwise-truncatable tail pages.

## References
- [Investigation
thread](https://sq-block.slack.com/archives/C0B2WT43BDH/p1787760831375989)
- [PostgreSQL table storage
parameters](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-STORAGE-PARAMETERS)

Generated with Codex

---

**Update Aug 27, 2026:** Hardened fresh `pgschema` bootstraps after
review.

- Renamed the shared post-apply script to
`reconcile-schema-after-pgschema.sql` and made it restore the heartbeat
reloption and singleton row idempotently.
- Added live catalog and row assertions that stop bootstrap when the
database does not match the required state.
- Updated every repository `pgschema apply` caller, including the
desktop release smoke path.
- Added a regression test that requires future `pgschema apply` callers
to run reconciliation.
- Documented the `pgschema` DML and storage-parameter limitation in
`AGENTS.md` and at the reconciliation entry point.
- Merged current `main` and renumbered this migration to 0034 after
`main` added migration 0033.

---------

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
2026-08-27 13:01:13 -04:00

228 lines
10 KiB
SQL

-- Reconcile schema details that pgschema does not preserve.
--
-- pgschema reconciles DDL, but it does not execute seed DML or preserve every
-- table storage parameter from schema/schema.sql. It also currently emits
-- partition children as standalone CREATE TABLE statements. Every pgschema
-- apply caller must run this idempotent script so fresh bootstraps converge on
-- the same live database contract as migration-managed databases.
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p_past'::regclass
) THEN
-- pgschema may copy parent triggers onto standalone children. Drop
-- those copies before ATTACH; PostgreSQL recreates inherited parent
-- triggers while attaching and rejects same-named child triggers.
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p_past;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p_past;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p_past;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p_past;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p_past;
ALTER TABLE events ATTACH PARTITION events_p_past
FOR VALUES FROM (MINVALUE) TO ('2026-01-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_01'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_01;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_01;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_01;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_01;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_01;
ALTER TABLE events ATTACH PARTITION events_p2026_01
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_02'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_02;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_02;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_02;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_02;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_02;
ALTER TABLE events ATTACH PARTITION events_p2026_02
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_03'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_03;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_03;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_03;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_03;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_03;
ALTER TABLE events ATTACH PARTITION events_p2026_03
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_04'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_04;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_04;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_04;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_04;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_04;
ALTER TABLE events ATTACH PARTITION events_p2026_04
FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_05'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_05;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_05;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_05;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_05;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_05;
ALTER TABLE events ATTACH PARTITION events_p2026_05
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p2026_06'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p2026_06;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p2026_06;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_06;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_06;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_06;
ALTER TABLE events ATTACH PARTITION events_p2026_06
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'events'::regclass
AND inhrelid = 'events_p_future'::regclass
) THEN
DROP TRIGGER IF EXISTS events_enqueue_push_match ON events_p_future;
DROP TRIGGER IF EXISTS events_refresh_channel_ttl ON events_p_future;
DROP TRIGGER IF EXISTS events_created_at_floor ON events_p_future;
DROP TRIGGER IF EXISTS community_write_fence_events ON events_p_future;
DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p_future;
ALTER TABLE events ATTACH PARTITION events_p_future
FOR VALUES FROM ('2026-07-01') TO (MAXVALUE);
END IF;
-- When pgschema creates partition children as standalone tables, it also
-- preserves the parent's identity column on delivery_log children. PostgreSQL
-- rejects attaching a child table that has its own identity column, so each
-- delivery_log attach path drops that standalone identity first. Raw
-- schema-created partitions are already attached, so these branches do not
-- run against inherited partition columns.
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p_past'::regclass
) THEN
ALTER TABLE delivery_log_p_past ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p_past;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p_past
FOR VALUES FROM (MINVALUE) TO ('2026-03-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p2026_03'::regclass
) THEN
ALTER TABLE delivery_log_p2026_03 ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_03;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_03
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p2026_04'::regclass
) THEN
ALTER TABLE delivery_log_p2026_04 ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_04;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_04
FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p2026_05'::regclass
) THEN
ALTER TABLE delivery_log_p2026_05 ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_05;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_05
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p2026_06'::regclass
) THEN
ALTER TABLE delivery_log_p2026_06 ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_06;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_06
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_inherits
WHERE inhparent = 'delivery_log'::regclass
AND inhrelid = 'delivery_log_p_future'::regclass
) THEN
ALTER TABLE delivery_log_p_future ALTER COLUMN id DROP IDENTITY IF EXISTS;
DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p_future;
ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p_future
FOR VALUES FROM ('2026-07-01') TO (MAXVALUE);
END IF;
END $$;
-- pgschema reconciles DDL but does not apply seed DML or table storage
-- parameters from schema/schema.sql. Restore those parts of the desired-state
-- contract explicitly and fail the bootstrap if the live catalog disagrees.
ALTER TABLE replica_heartbeat SET (vacuum_truncate = false);
INSERT INTO replica_heartbeat (id) VALUES (1)
ON CONFLICT (id) DO NOTHING;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_class AS relation
JOIN pg_namespace AS namespace ON namespace.oid = relation.relnamespace
WHERE namespace.nspname = current_schema()
AND relation.relname = 'replica_heartbeat'
AND COALESCE(
relation.reloptions @> ARRAY['vacuum_truncate=false']::text[],
false
)
) THEN
RAISE EXCEPTION 'replica_heartbeat must disable vacuum truncation after pgschema apply';
END IF;
IF (SELECT count(*) FROM replica_heartbeat WHERE id = 1) <> 1 THEN
RAISE EXCEPTION 'replica_heartbeat must contain its singleton row after pgschema apply';
END IF;
END $$;