Giant Eye Tech — An Eye in the Sky
Giant Eye TechAn Eye in the Sky

Zero-Downtime Database Schema Migrations on Live Kubernetes Clusters

The Expand and Contract pattern for non-breaking relational database migrations: running multi-version microservices side-by-side.

Giant Eye Tech DevOps Practice(Cloud Infrastructure & SRE Lead)
June 2026
10 min read

Outage Time

0.00 Seconds

Zero dropped client requests during cutover

Migration Safety

100% Reversible

Instant rollback possible at any stage

Table Lock Duration

< 15 ms

Pure catalog lock vs whole-table scan

01The Engineering Bottleneck

Renaming columns, modifying table foreign keys, or altering non-null constraints with standard ORM migration tools (Prisma, Django, TypeORM, Rails) takes exclusive AccessExclusiveLock locks in PostgreSQL. On tables with millions of rows, this causes HTTP 504 timeouts, connection pileups, and system outages for customer-facing ERP and e-commerce platforms.

02Architectural Design & Invariants

We enforce the Expand and Contract pattern across continuous deployment pipelines. Migrations are split into decoupled phases: Phase 1 (Expand) adds new nullable columns and dual-writes. Phase 2 backfills historical rows in non-locking batches. Phase 3 switches reading microservices. Phase 4 (Contract) safely deprecates and drops unused legacy columns after zero active versions reference them.

03Production Implementation Blueprint

sql
Safe Concurrent Indexing & Phased Column Migration
Strict Production Invariant
-- Step 1: NEVER run standard CREATE INDEX on a live production table
-- It locks writes for the entire duration of index creation.
-- INSTEAD: Always use CONCURRENTLY in PostgreSQL
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_invoices_customer_status
ON invoices (customer_id, payment_status)
WHERE payment_status != 'archived';

-- Step 2: Adding a new required column without downtime
-- NEVER do: ALTER TABLE users ADD COLUMN phone_e164 TEXT NOT NULL;
-- (This locks the entire table while rewriting all rows with default values)

-- INSTEAD: Add as nullable first (instant catalog update)
ALTER TABLE users ADD COLUMN phone_e164 TEXT;

-- Step 3: Add validation constraint without holding exclusive table locks
ALTER TABLE users 
ADD CONSTRAINT check_phone_format 
CHECK (phone_e164 ~ '^+[1-9]d{1,14}$') 
NOT VALID; -- NOT VALID applies check only to new writes immediately

-- Step 4: Validate existing historical data in background without locking writes
ALTER TABLE users VALIDATE CONSTRAINT check_phone_format;

04Architectural Invariants & Rules of Thumb

  • Treat database migrations as multi-stage distributed rollouts rather than atomic CI/CD step commands.
  • Always use `CREATE INDEX CONCURRENTLY` in PostgreSQL to prevent read/write blocking on production tables.
  • Never alter column data types in a single step; write to both columns during cutover and verify with dark traffic reading.
  • Kubernetes `preStop` hooks and `terminationGracePeriodSeconds` must allow active HTTP transactions to complete cleanly before SIGKILL.
Enterprise Systems Practice

Facing similar architecture bottlenecks in your business?

We design and implement custom ERPs, high-throughput databases, and air-gapped private AI systems tailored for high-concurrency enterprise workloads.