Companion code for Zero-Downtime Database Migrations: Expand-Contract in Practice with Spring Boot: a full expand/migrate-writes/migrate-reads/contract sequence run as an actual rolling deploy across two live replicas, with a load generator sending continuous HTTP traffic through all four deploys (99.98% success, every residual error traced to a root cause rather than left unexplained). Findings include a real NOT NULL constraint trap in the expand migration, a backfill-window bug in the read switch, H2's AUTO_SERVER=TRUE single-point-of-failure behavior under a rolling restart, the drain-before-SIGTERM fix needed to close a health-check gap during graceful shutdown, and H2 silently discarding a concurrently committed INSERT during an ALTER TABLE ADD/DROP COLUMN rebuild - confirmed, by primary source, to be an H2-specific behavior rather than a property of the technique itself. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019Fb7vW8vLyLKngBc4R3huA
4.2 KiB
8. The rolling-window proof
← 7. The backfill window bug · Next: 9. The contract migration →
Every rolling deploy in this sequence — Deploy 2, Deploy 3, Deploy 4a — has a window
where two adjacent stages are serving real traffic against the same database at the
same time. MixedStageRollingWindowTest
is the property this whole article rests on, checked directly: it builds two
CustomerService instances on adjacent stages sharing one database, and cross-checks
every write/read direction across all three rollouts.
-- Stage 1 writes, Stage 2 reads --
Customer[id=1, name=Radia Perlman, [email protected]]
-- Stage 2 writes, Stage 1 reads --
Customer[id=2, name=Barbara Liskov, [email protected]]
-- Stage 2 writes, Stage 3 reads --
Customer[id=3, name=Shafi Goldwasser, [email protected]]
-- Stage 3 writes, Stage 2 reads --
Customer[id=4, name=Frances Allen, [email protected]]
-- Stage 3 writes, Stage 4 reads --
Customer[id=5, name=Adele Goldberg, [email protected]]
-- Stage 4 writes, Stage 3 reads --
Customer[id=6, name=Karen Sparck Jones, [email protected]]
Full transcript:
docs/output/08-mixed-stage-rolling-window.txt.
Six pairs, six passing reads. If any one of them failed, the technique would not be zero-downtime for that rollout — it would just be a race against however long the rollout takes to finish, with correctness depending on luck rather than design.
Deploy 3 rollout Stage 2 (draining) Stage 3 (arriving) ← overlap: both true, both correct
Deploy 4a rollout Stage 3 (draining) Stage 4 (arriving) ← overlap: both true, both correct
The diagram is the same shape three times because the guarantee is the same three times: whichever two stages are live together during a given rollout, a write from either one has to be readable correctly by the other. That's what the test above checks directly, and it's what the article's live 4-deploy run — a real load generator, hitting real HTTP endpoints, during a real rolling restart — is reproducing under actual timing pressure rather than a unit test's controlled ordering.
Going deeper
- The live version of this proof, with two real replicas and continuous HTTP
traffic: chapter 11 and the full run in
docs/output/11-live-deploy-sequence.txt.
← 7. The backfill window bug · Next: 9. The contract migration →