dorfteich/docs/vs-nfd/98-update-rollback-protokoll.md
Claude Fable 5 18239e2fa9
All checks were successful
CD / Deploy to Test (push) Successful in 12s
CD / Smoke tests against Test (push) Successful in 1m15s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Successful in 6m19s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m13s
CI / Import/export fidelity gate (push) Successful in 54s
CI / Lint, typecheck, test (pull_request) Successful in 6m20s
CI / Build container images (pull_request) Successful in 1m12s
CI / Auth e2e pack (pull_request) Successful in 8m24s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Build and push images (push) Successful in 22s
#221: offline update path incl. migrations, rehearsed with rollback
Adds docs/operations/update-runbook.md (obtain, verify by digest, back
up, apply, verify, roll back) with the migration behaviour stated
explicitly: a failed migration rolls back its own transaction but is
recorded in _prisma_migrations and blocks every further migrate deploy
(P3009) — including a re-deployed old image — until migrate resolve
--rolled-back; semantically irreversible migrations have exactly one way
back, the pre-update backup set. No rolling updates on a compose stage.
Rehearsed in the isolated environment of #220: regular update to a v2
image set, then a deliberate failed-update (P3018 division by zero,
schema change proven rolled back) with image-rollback-alone shown
insufficient and the documented recovery executed. Protocol:
docs/vs-nfd/98-update-rollback-protokoll.md. ADR 0024 decisions 5+6
recorded as executed; operations handbook and restore runbook updated;
plan checkbox P1-3 ticked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8
2026-07-31 17:26:50 +02:00

78 lines
4.4 KiB
Markdown

# Update-/Rollback-Protokoll — Probe in der isolierten Umgebung (Issue #221)
Nachweis für Maßnahmenplan P1-3 (ADR 0024): der Update-Pfad inklusive
Migrationen und geprobtem Fehlschlag-Rollback, ausgeführt in derselben
netzisolierten Umgebung wie der Isolationslauf
(`97-isolationslauf-protokoll.md` — Compose-Projekt `dorfteich-isolated`,
alle Netze `internal: true`, kein Egress-Pfad). Belegstufe: **live
verifiziert**. Die daraus destillierte Prozedur steht in
`docs/operations/update-runbook.md`.
- **Datum:** 2026-07-31
- **Ausgangsstand („v1"):** Images `dorfteich-iso-*:isolated`
(`main@a758c9d`, backup mit #288-Fix aus `4f6596e`), Instanz mit
Inhalt aus dem Isolationslauf.
- **Update-Ziel („v2"):** Images `dorfteich-iso-*:isolated-v2`, gebaut
aus dem Kettenstand `ccffcaa` (#288 + #220) — als Stellvertreter eines
regulären Release-Bundles; die Beschaffung/Digest-Prüfung eines echten
Bundles ist das Mirror-Verfahren (#218, `95-mirror-protokoll.md`) und
wurde dort bereits nachgewiesen.
- **Fehlschlag-Kandidat („bad"):** api-Image mit einer absichtlich
fehlschlagenden Migration
(`20260731230000_deliberately_failing_update`: `ALTER TABLE` +
`SELECT 1/0`), nur für die Probe gebaut, nie committet.
## Phase A — reguläres Update
| Schritt | Ergebnis |
| ------------------- | --------------------------------------------------------------------------------------------- |
| Pre-Update-Backup | ✅ Set `20260731-150833` über die Admin-API erzeugt und in der Set-Liste bestätigt |
| `TAG` → v2, `up -d` | ✅ alle vier eigenen Services zusammen ersetzt (Ausfallfenster Sekunden, kein Rolling Update) |
| Migrationen | ✅ migrate-on-start läuft durch (dieses „Release" enthält keine neuen Migrationen) |
| `/readyz` | ✅ vollständig grün |
| Inhalt | ✅ Seite „Isolationstest Seite" unverändert vorhanden |
## Phase B — geprobter Fehlschlag + Rollback
1. **Bad-Update einspielen** (`TAG` → bad, `up -d`): api scheitert beim
Boot in `migrate deploy`**P3018**, `ERROR: division by zero` bei
`20260731230000_deliberately_failing_update`; Container geht in den
Restart-Loop, jeder Folge-Boot meldet **P3009** („migrate found
failed migrations").
2. **Datenbank-Zustand geprüft:** die Migration steht als `failed` in
`_prisma_migrations` (Logs erfasst), aber `broken_column` existiert
**nicht** — die per-Migration-Transaktion hat den Schema-Eingriff
zurückgerollt. Genau das dokumentierte Verhalten.
3. **Image-Rollback allein reicht nicht** (bewusst vorgeführt): `TAG`
zurück auf v2, `up -d` — der alte Stand scheitert weiter mit
**P3009** („The 20260731230000_deliberately_failing_update migration
started at 2026-07-31 15:10:50 UTC failed").
4. **Recovery wie im Runbook:**
`migrate resolve --rolled-back 20260731230000_deliberately_failing_update`
(im api-Image ausgeführt) → „marked as rolled back" → api-Neustart:
`/readyz` vollständig grün, Inhalt unverändert, Migrations-Row trägt
`rolled_back_at`.
5. **Alternativer Weg zurück** (für semantisch irreversible, aber
erfolgreich gelaufene Migrationen): Restore des Pre-Update-Sets — der
Restore-Pfad selbst ist im Isolationslauf verifiziert
(`97-isolationslauf-protokoll.md`, Fix #288).
## Versions-Skew (Akzeptanzkriterium)
Ein Compose-Stage kennt **kein Rolling Update**: `docker compose up -d`
ersetzt web/api/collab/backup zusammen; Mischbetrieb alter und neuer
Versionen ist kein unterstützter Betriebszustand und dauert nur die
Container-Ersetzung selbst (Sekunden). Für VS-Zonen heißt das: Update im
angekündigten Wartungsfenster; die Migrationen wendet ausschließlich der
neue api-Container beim Start an (`MIGRATE_ON_START`).
## Rückfluss
- **`docs/operations/update-runbook.md`** — die Prozedur (Beschaffen,
Prüfen, Sichern, Einspielen, Verifizieren, Rollback) inkl. explizitem
Migrationsverhalten; aus dieser Probe destilliert.
- **`docs/operations/restore-runbook.md`** — Querverweis: Restore als
einziger Weg zurück nach irreversiblen Migrationen.
- **#229** — Betriebshandbuch §1 (Airgap) und §2 (Update/Rollback)
verweisen auf Runbook und dieses Protokoll.