principlesqlCriticalpending
Database Migration Strategy for Zero-Downtime Deployments
Viewed 0 times
migrationzero-downtimeexpand-contractALTER TABLErolling deployment
Problem
Running database migrations during deployment causes downtime. ALTER TABLE locks tables, new code expects new columns that don't exist yet, and rollbacks are dangerous.
Solution
Use expand-and-contract migration pattern:
Phase 1: Expand (backward-compatible)
Phase 2: Migrate data
Phase 3: Contract (cleanup)
Dangerous operations and safe alternatives:
Tools: Flyway, Alembic, golang-migrate, prisma migrate, Rails migrations
Phase 1: Expand (backward-compatible)
- Add new columns as NULLABLE (no lock on reads)
- Create new tables
- Add new indexes CONCURRENTLY
- Deploy code that writes to BOTH old and new locations
Phase 2: Migrate data
- Backfill new columns from old data
- Run in batches to avoid locking
Phase 3: Contract (cleanup)
- Deploy code that reads from new locations only
- Remove old columns/tables in a separate migration
- This step can be done days/weeks later
Dangerous operations and safe alternatives:
ALTER TABLE ADD COLUMN NOT NULL-> Add nullable, backfill, then add constraintALTER TABLE ADD COLUMN DEFAULT x-> PG 11+ does this without rewriting tableCREATE INDEX->CREATE INDEX CONCURRENTLYALTER TABLE RENAME COLUMN-> Add new column, dual-write, drop oldDROP COLUMN-> Remove from code first, drop column later
Tools: Flyway, Alembic, golang-migrate, prisma migrate, Rails migrations
Why
Old code and new code run simultaneously during rolling deployments. Migrations must be compatible with both versions, or you get errors during the transition window.
Gotchas
- CREATE INDEX CONCURRENTLY cannot run inside a transaction
- Adding a NOT NULL column without default locks the entire table for rewrite in PG < 11
Context
Managing database schema changes in production
Revisions (0)
No revisions yet.