Checklist: YSD-22058 (rollout) · drill kind rollout Decision owner
role: the service owner for svc-study-workspace Machinery:
apps/yemaya/svc-study-workspace/src/persistence/migrate.ts
The nearest thing to this that existed was a lock-timeout docblock in
migrate.ts, addressed to whoever edits migrations. It is good and it is about
one schema change. Nothing described putting a service build in front of
traffic in stages, what signal halts it, or who decides — and having nobody to
name is how a rollout continues past the first bad signal.
Running this document IS the drill. Record it in
procedure-exercise-register.json.
Detection — what tells you a stage is going wrong#
Not "a customer tells us". In order of how early they fire:
- The migration refused to apply. Every migration runs under
lock_timeoutand aborts transactionally and unrecorded when it queues behind a held lock. This is by design and is not a failure: the rollout retries after the blocking work clears. Treat repeated aborts as a signal that something long-running holds a lock, not as a reason to raise the timeout. - Readiness stops passing on the new stage (
readiness.ts). - Error rate or latency on the new stage diverges from the old one. The comparison is against the stage still serving, not against an absolute — a deploy during a traffic spike looks bad on absolutes and fine on the diff.
The stages, and who decides#
| # | Action | Owner role | Expected |
|---|---|---|---|
| 1 | Apply migrations against production, forward-only | service owner | 5 min |
| 2 | Bring one instance of the new build up, serving no traffic; confirm readiness | on-call engineer | 5 min |
| 3 | Move a small share of traffic to it; hold and watch the three signals above | on-call engineer | 15 min |
| 4 | Complete the rollout, or halt and go to yemaya-study-rollback.md |
service owner decides | 10 min |
Rollout time objective: 45 minutes, from applying the migration to either
completing or halting. Set 2026-08-15, and derived rather than chosen: the four
stages above are budgeted at 5 + 5 + 15 + 10 = 35, and the objective is that
plus ten minutes of headroom, rounded up to the next five — the same rule
yemaya-study-recovery.md and yemaya-study-rollback.md use. An objective with
no headroom over the plan meant to meet it is missed by any run that hits one
snag, and one without an objective at all cannot be drilled: evaluateDrill
refuses a record that has no duration to be measured against.
The objective is not met by halting quickly. Halting is a legitimate outcome of stage 4 and stops the clock; going past 45 minutes still holding traffic at a stage is the miss.
Step 4 is the one this document exists for. The decision to continue is a decision, taken by a named role, on the signals in front of them. A rollout that proceeds because nobody said stop has not been decided.
Forward-only#
migrate.ts treats a checksum change on an applied migration as a hard error.
There is no "edit the migration and redeploy": a fix ships as a new migration
file. If you are tempted to amend one, you are about to make two databases
disagree about what has run.
Communication#
| Audience | Within |
|---|---|
| The on-call channel — rollout started, and each stage entered | immediately |
| Affected tenants, if a stage is halted | 30 min |
Done means#
- Every instance serves the new build, readiness passes, and the three signals match the pre-rollout baseline.
study_schema_migrationsholds exactly the migrations the build expects.- The result is recorded in the procedure exercise register, naming who ran it, the environment, and how long each stage took.
Known shortfall#
The migration half is exercised on every change by
migrations-rollout.integration.spec.ts, which applies a real migration over
seeded volume against live PostgreSQL with a concurrent writer and checks for
stalls. The build half — staged traffic, the halt signal, the decision — is
exercised by nothing. Until a drill works through it, this document describes
an intended procedure rather than a rehearsed one, and that gap is the whole
distinction the register is keeping.