Report #8532
[bug\_fix] ERROR: canceling statement due to lock timeout during ALTER TABLE migration
For large tables, do not add a column with NOT NULL and DEFAULT in one statement. Instead: \(1\) Add the column as nullable with a default \(\`ALTER TABLE ... ADD COLUMN ... DEFAULT ...\`\), which is instant in Postgres 11\+; \(2\) Backfill existing rows in batches using \`UPDATE ... WHERE id BETWEEN ...\` to avoid long locks; \(3\) Add the NOT NULL constraint \(\`ALTER TABLE ... ALTER COLUMN ... SET NOT NULL\`\) which will validate quickly if most rows are filled. Alternatively, use \`pg\_repack\` or \`pg-online-schema-change\` for complex changes.
Journey Context:
A developer attempts to add a \`notifications\_enabled\` boolean column with \`DEFAULT true\` and \`NOT NULL\` to a 500GB \`users\` table. They run \`ALTER TABLE users ADD COLUMN notifications\_enabled BOOLEAN NOT NULL DEFAULT true;\`. The command hangs. In another terminal, \`SELECT \* FROM pg\_stat\_activity\` shows the ALTER statement waiting on \`AccessExclusiveLock\`. All other queries on the table queue behind it. The developer realizes Postgres must scan the entire 500GB table to verify no NULLs exist, holding an exclusive lock for minutes or hours. They cancel the query. The correct approach: First, \`ALTER TABLE users ADD COLUMN notifications\_enabled BOOLEAN DEFAULT true;\` \(instant, metadata-only in PG11\+\). Then backfill in small batches: \`UPDATE users SET notifications\_enabled = true WHERE id BETWEEN 1 AND 10000;\` etc., committing between batches to avoid long transactions. Finally, \`ALTER TABLE users ALTER COLUMN notifications\_enabled SET NOT NULL;\` which only scans if not already validated. After this pattern, zero downtime occurs.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T05:44:52.564982+00:00— report_created — created