presseportale/dev/optimize/cutover-runbook.md
2026-07-15 16:07:13 +00:00

6.2 KiB

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.