docs: Cutover-Runbook für den Go-Live (Legacy-Import & Umschaltung)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3e855375b1
commit
9c67fc6c14
1 changed files with 128 additions and 0 deletions
128
dev/optimize/cutover-runbook.md
Normal file
128
dev/optimize/cutover-runbook.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Cutover-Runbook — Go-Live Pressekonto
|
||||
|
||||
Stand: 2026-07-14 · Branch `fix/zeitzone-europe-berlin`
|
||||
|
||||
Abhakbare Checkliste für die Umschaltung vom Alt-System auf Pressekonto.
|
||||
Alle Commands laufen über Sail: `vendor/bin/sail artisan …` (bzw. ohne Sail
|
||||
`php artisan …`, je nach Deployment-Umgebung).
|
||||
|
||||
---
|
||||
|
||||
## 0. Grundverständnis (einmal lesen)
|
||||
|
||||
- **Zwei Legacy-Quellen, unterschiedlicher Charakter:**
|
||||
- **Presseecho** = echte **Live-DB** (`mysql_presseecho`). Nur hier greift die
|
||||
„offline nehmen → Delta nachziehen"-Choreografie.
|
||||
- **Businessportal24** = **statischer Snapshot-Dump** (`mysql_businessportal`).
|
||||
Ändert sich nicht mehr → einmal importieren reicht, kein Delta nötig.
|
||||
- **Idempotenz:** Alle Importer schreiben per `updateOrCreate(legacy_portal, legacy_id)`
|
||||
+ Crosswalk-Tabelle `legacy_import_map`. Mehrfaches Ausführen erzeugt **keine
|
||||
Duplikate**. Bei Absturz mittendrin: einfach erneut starten (kein Rollback,
|
||||
Sicherheit kommt aus Idempotenz, nicht aus Transaktionen).
|
||||
- **Standard-Lauf überspringt bereits importierte Zeilen.** Zwischenzeitliche
|
||||
**Änderungen** an Altdatensätzen werden nur mit `--force` nachgezogen.
|
||||
- **Kein Delete-Sync:** Im Alt-System gelöschte Datensätze bleiben im neuen
|
||||
System bestehen. Für das kurze Cutover-Fenster i. d. R. vernachlässigbar.
|
||||
- ⚠️ **`migrate:presseecho` NICHT ausführen** — toter, kaputter Stub. Der einzige
|
||||
gültige Weg ist `legacy:import`.
|
||||
- **Voraussetzung Timestamp-Sync:** `legacy:fix-timestamps` nutzt Cross-Schema-
|
||||
JOINs → beide Legacy-Schemas müssen auf **demselben MySQL-Server** liegen wie
|
||||
die App-DB. Sonst diesen Schritt durch vollen `--force`-Reimport ersetzen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Vorbereitung (Tage vorher, in Ruhe)
|
||||
|
||||
- [ ] Prod-`.env` gesetzt: `APP_ENV=production`, `APP_DEBUG=false`,
|
||||
`SESSION_SECURE_COOKIE=true`, `QUEUE_CONNECTION=database` (o. Ä. dauerhaft).
|
||||
- [ ] Legacy-Zugangsdaten in `.env` eingetragen:
|
||||
`DB_HOST_PRESSEECHO / DB_DATABASE_PRESSEECHO / DB_USERNAME_PRESSEECHO / DB_PASSWORD_PRESSEECHO`
|
||||
und `DB_*_BUSINESSPORTAL` (Snapshot-Dump auf denselben Server eingespielt).
|
||||
- [ ] Secrets rotiert (OpenAI / Google / Mail) — nicht die Alt-Keys übernehmen.
|
||||
- [ ] Legacy-Medien ins lokale Staging kopiert:
|
||||
`dev/migration/<portal>/uploads/...` (rsync vom Alt-System).
|
||||
- [ ] DB-Migrationen ausgeführt: `artisan migrate --force`.
|
||||
Vorher prüfen: keine Duplikate in `invoices.stripe_invoice_id`
|
||||
(Unique-Migration bricht sonst bewusst ab).
|
||||
- [ ] Rollen/Permissions ausgerollt: `artisan db:seed --class=RolesAndPermissionsSeeder`.
|
||||
- [ ] **Verbindungstest / Zählwerte:** `artisan legacy:import --source=all --dry-run`
|
||||
→ erwartete Zeilenzahlen plausibel?
|
||||
|
||||
---
|
||||
|
||||
## 2. Bulk-Import (Presseecho noch live, ohne Downtime)
|
||||
|
||||
Reihenfolge ist in `legacy:import --step=all` hart verdrahtet
|
||||
(categories → users → companies → contacts → press-releases → link-associations).
|
||||
|
||||
- [ ] `artisan legacy:import --source=all`
|
||||
(idempotent — beliebig oft wiederholbar).
|
||||
- [ ] `artisan legacy:migrate-media --portal=all`
|
||||
(Datei-Bytes aus `dev/migration/...` → **public**-Disk + Varianten).
|
||||
- [ ] **`artisan press-releases:privatize-media`**
|
||||
(verschiebt PM-Medien public → **private** Disk — Security-Hardening;
|
||||
MUSS nach `legacy:migrate-media` laufen).
|
||||
- [ ] `artisan legacy:fix-timestamps`
|
||||
(created_at/updated_at + published_at per Cross-DB-JOIN).
|
||||
- [ ] Satelliten-Migrationen (Reihenfolge unkritisch, jeweils `--dry-run` zuerst):
|
||||
- [ ] `artisan legacy:import-api-keys`
|
||||
- [ ] `artisan legacy:archive-invoices`
|
||||
- [ ] `artisan legacy:grandfather-subscriptions`
|
||||
- [ ] `artisan legacy:import-billing-addresses`
|
||||
- [ ] `artisan legacy:migrate-agency-names`
|
||||
- [ ] **Gate:** `artisan legacy:verify` → JSON-Report prüfen. Go / No-Go.
|
||||
|
||||
---
|
||||
|
||||
## 3. Cutover-Fenster (kurze Downtime)
|
||||
|
||||
Ziel: nur die seit dem Bulk-Import geänderten/neuen **Presseecho**-Daten
|
||||
nachziehen. Businessportal24 braucht hier nichts (Dump ist statisch).
|
||||
|
||||
- [ ] **Presseecho** in **Read-Only / Wartung** schalten — **Lesezugriff behalten**,
|
||||
der Delta-Import muss noch aus der Legacy-DB lesen können.
|
||||
- [ ] Neues System in Wartung: `artisan down`.
|
||||
- [ ] **Delta-Import mit `--force`** (zieht auch geänderte Bestandsdatensätze nach;
|
||||
uuid + slug von PMs bleiben stabil → öffentliche URLs brechen nicht):
|
||||
`artisan legacy:import --source=presseecho --force`
|
||||
- [ ] Medien-Delta: `artisan legacy:migrate-media --portal=presseecho --force`
|
||||
(`--force` erzwingt Neukopie + Varianten geänderter Bilder) →
|
||||
danach erneut `artisan press-releases:privatize-media`.
|
||||
- [ ] `artisan legacy:fix-timestamps --portal=presseecho`
|
||||
- [ ] **Gate:** `artisan legacy:verify --portal=presseecho`.
|
||||
- [ ] **DNS umstellen** auf das neue System.
|
||||
- [ ] Queue-Worker läuft & wird überwacht → Admin ▸ **Reports ▸ Betriebsüberwachung**
|
||||
(alle Kacheln grün, besonders „Queue-Worker" und „Medien-Privatisierung").
|
||||
- [ ] `artisan up` — neues System live.
|
||||
- [ ] Alt-System **komplett offline**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Go-Live-Kommunikation
|
||||
|
||||
- [ ] Passwort-Reset-Mails versenden (Importer setzt **bewusst keine Passwörter**):
|
||||
erst `artisan auth:send-go-live-mails --dry-run` (Anzahl prüfen),
|
||||
dann `artisan auth:send-go-live-mails`.
|
||||
- ⚠️ **Massen-Mailversand über die Queue** → vorher Worker-Status grün prüfen,
|
||||
während des Versands Betriebsüberwachung im Blick behalten
|
||||
(pending/failed jobs).
|
||||
|
||||
---
|
||||
|
||||
## 5. Nachlauf / Verifikation
|
||||
|
||||
- [ ] Stichproben: Login mit Reset-Link, PM-Detailseite (Bild + Anhang laden),
|
||||
Newsroom-Übersicht, Archiv-Sichtbarkeit.
|
||||
- [ ] Betriebsüberwachung: keine `failed_jobs`, kein „gestoppter Worker".
|
||||
- [ ] `storage/logs` auf Fehler prüfen.
|
||||
- [ ] Backup der neuen Prod-DB direkt nach erfolgreichem Cutover.
|
||||
|
||||
---
|
||||
|
||||
## Offene Punkte / bewusste Entscheidungen
|
||||
|
||||
- **Gelöschte Legacy-Datensätze** werden nicht synchronisiert (kein Delete-Sync).
|
||||
Falls im Parallelbetrieb im Alt-System gelöscht wird → im neuen System nachher
|
||||
manuell prüfen (i. d. R. vernachlässigbar bei kurzem Fenster).
|
||||
- **`legacy:fix-timestamps`** setzt voraus, dass beide Legacy-Schemas auf demselben
|
||||
MySQL-Server liegen. Andernfalls voller `--force`-Reimport statt Timestamp-Fix.
|
||||
Loading…
Add table
Add a link
Reference in a new issue