Files
HiveGrid-Wireframes/hivegrid-cloud/docs/improvement-plan/03-scaling-infrastructure

Phase 03 — Skalierungs-Infrastruktur

Historical phase-plan status: this README preserves the original Phase 03 dependency map and done definition. Current runtime/operator truth lives in docs/improvement-plan/PROGRESS.md, docs/improvement-plan/OPERATOR-CHECKLIST.md, docs/ops/worker-queues.md, docs/architecture/outbox-pattern.md, and docs/ops/asset-cdn.md. In particular, #51/A7/B2 CDN rollout is currently parked in backlog, not an active Phase 03 closeout item.

Dauer: 4-5 Wochen Tasks: 12 (dieses README dokumentiert alle 12; die State-/DB-Teilmenge 37-42 ist hier ausführlich, Worker/Backup/CDN 43-47/51 in separaten Detail-MDs vom Parallel-Subagent) Ziel: Boilerplate von „Single-Process-MVP" auf horizontal skalierbare Production-Architektur bringen. Alle In-Process-State-Stores raus, DB-Stack härten (PgBouncer + Read-Replica + Outbox), Worker-Queues sauber trennen, Backups + Tenant-Snapshots automatisieren, statische Assets über CDN ausliefern.

Warum diese Phase

Aktueller Zustand: Mehrere kritische Code-Pfade halten Zustand im Prozess-RAM (apps/api/src/auth/login-rate-limit.ts, apps/web/app/utils/auth/mfa/challenge-rate-limit.server.ts, apps/api/src/idempotency/store.ts, apps/web/app/utils/services/.server/subscriptionService.ts mit cachified-Wrapping, apps/api/src/auth/session/session-store.ts, apps/web/app/modules/mcp/mcp.server.ts). Sobald eine zweite Replica läuft, sind Rate-Limits umgehbar, Idempotency-Keys werden inkonsistent, Stripe-Webhook-Cache-Invalidations erreichen nur eine Instanz und Sessions ghosten.

DB-seitig hängen 100+ Prisma-Calls direkt am Primary, ohne Connection-Pooler. Bei 200 gleichzeitigen Web-Requests reicht ein Tab voll schwerer Dashboard-Loaders, um Postgres-max_connections auszuschöpfen. Außerdem laufen alle Reads gegen den Writer, und Stripe-Webhook-Side-Effects (Mail, Webhook, Audit-Log) sind nicht transaktional mit dem DB-Write gekoppelt — bei Crash zwischen Commit und Side-Effect verlieren wir Outbound-Events.

Phase 03 baut die fehlende Plumbing für Multi-Replica-Betrieb. Ohne diese Phase blockieren wir Phase 04 (Search braucht Outbox für async-Indexing) und Phase 05 (Feature-Flags brauchen einen Shared-Cache mit Pub/Sub-Invalidation).

Task-Reihenfolge

Reihenfolge Task-ID Datei Parallelisierbar mit Subagent-Slot
1 #37 shared-store-package.md — (Foundation für alles andere) State-Subagent
2 #38 shared-store-migration.md #40, #43, #51 State-Subagent
3 #39 pubsub-cache-invalidation.md #41, #44, #45 State-Subagent
4 #40 pgbouncer.md #38, #43, #45 State-Subagent (DB-Teil)
5 #41 read-write-split.md #39, #44, #46, #51 State-Subagent (DB-Teil)
6 #42 outbox-pattern.md #45, #47 State-Subagent (DB-Teil)
7 #45 worker-queues.md #39, #40, #41 Worker-Subagent
8 #46 streaming-exports.md #41 Worker-Subagent
9 #47 job-priority.md #42 Worker-Subagent
10 #43 backup-cli.md #38, #40 Worker-Subagent
11 #44 tenant-snapshot.md #39, #41, #43 Worker-Subagent
12 #51 asset-cdn.md #38, #41 Worker-Subagent; currently backlog/operator-deferred

Dependency-Graph (intern)

#37 shared-store-package ──► #38 shared-store-migration ──► #39 pubsub-cache-invalidation ─┐
        │                                                                                    │
        └─────────────────► (nutzt Redis-Connection auch für BullMQ-Queues)                  │
                                                                                             │
#40 pgbouncer ──► #41 read-write-split ──► #42 outbox-pattern ──────────────────────────────┤
                                                  │                                          │
                                                  ▼                                          │
                                          #47 job-priority                                   │
                                                                                             │
#45 worker-queues ──► #46 streaming-exports                                                  │
                                                                                             │
#43 backup-cli ──► #44 tenant-snapshot                                                       │
                                                                                             │
#51 asset-cdn (parked/backlog) ──────────────────────────────────────────────────────────────┤
                                                                                             │
                                                                                             ▼
                                                                                       Phase 04 (Search/RAG)

Wichtige Reihenfolge-Regeln:

  • #37 shared-store-package ist hartes Fundament: liefert das Adapter-Interface, die Redis-/Memory-Implementations und die Higher-Level-Helpers (Rate-Limit, Cache, Session, Idempotency). Ohne dieses Package kann weder #38 noch #39 starten, und die Worker-Tasks (BullMQ teilt sich die Redis-Connection) werden auch davon abhängen.
  • #38 shared-store-migration migriert die 6 In-Process-Stores. Muss nach #37, parallel zu #40 möglich.
  • #39 pubsub-cache-invalidation ist die logische Fortsetzung der Migration für Caches mit nicht-trivialer Konsistenz (Stripe-Subscription-Cache, Tenant-Cache, Feature-Flag-Cache). Setzt #38 voraus, weil dort der Pub/Sub-Adapter etabliert wird.
  • #40 pgbouncer ist unabhängig von Shared-Store, muss aber vor #41 read-write-split stehen, weil R/W-Split die DIRECT_URL-Konvention für Migrations braucht, die in #40 etabliert wird.
  • #42 outbox-pattern braucht #41 (publishOutboxEvent läuft innerhalb einer Transaction auf prismaWrite), und liefert die Foundation für #47 job-priority (priorisierte Dispatcher-Queue) sowie Phase 04 (async-Indexing).
  • #45 worker-queues und #43 backup-cli sind weitgehend orthogonal und können parallel laufen, sobald #37 fertig ist. #51 asset-cdn war ebenfalls orthogonal, ist aber seit der Operator-Entscheidung 2026-05-24 in A7/B2/#51 geparkt; nicht als aktiven Phase-03-Blocker zählen.

Sub-Phasen-Verteilung

State-Subagent (dieses README behandelt 37-42 ausführlich):

  • #37 Shared-Store Package
  • #38 Shared-Store Migration
  • #39 Pub/Sub Cache-Invalidation
  • #40 PgBouncer Setup
  • #41 R/W-Split via Prisma $extends
  • #42 Outbox-Pattern + Dispatcher

Worker-Subagent (separate Detail-MDs, eigene Verantwortung):

  • #45 Worker-Queue-Trennung
  • #46 Streaming-Exports + Row-Cap
  • #47 BullMQ Job-Priority
  • #43 Backup-CLI
  • #44 Tenant-Snapshot-Tool
  • #51 Asset-CDN (historisch geplant; aktueller Status: backlog/operator-deferred)

Beide Subagents synchronisieren auf der Redis-Connection-Konvention aus #37 (gleiche ioredis-Instanz, unterschiedliche logical-DB-Indizes; BullMQ-Queues auf Index 0, Shared-Store auf Index 1, Pub/Sub auf Index 2).

Phase Done-Definition

Diese Phase ist completed wenn:

  • Alle 12 Tasks completed in PROGRESS.md
  • pnpm lint, pnpm typecheck, pnpm test global grün; testcontainers-basierte Integration-Tests laufen in CI gegen Redis + Postgres + PgBouncer
  • Staging deployed mit 2x Web-Replicas + 1x API + 2x Worker hinter Caddy-Reverse-Proxy; Last-Test (k6 mit 500 RPS auf 5min) zeigt: keine Login-Rate-Limit-Bypasses, keine Stripe-Cache-Inconsistencies, kein Connection-Exhaustion auf Postgres
  • Stripe-Webhook → Subscription-Cache-Invalidation < 2s end-to-end (vorher: bis zu 1h stale)
  • PgBouncer-SHOW STATS zeigt cl_active < default_pool_size unter normaler Last; Connection-Reuse-Ratio > 90 %
  • Read-Replica führt > 60 % der Dashboard-Reads aus (gemessen via Prisma-Middleware-Counter)
  • Outbox-Dispatcher hat 0 unverarbeitete Events > 60s alt im 24h-Monitor-Fenster
  • .env.example + .env.production.example enthalten alle neuen Keys (REDIS_URL, REDIS_SHARED_STORE_DB, REDIS_PUBSUB_DB, DATABASE_URL, DATABASE_READ_URL, DIRECT_URL) sauber dokumentiert
  • Doku-Files angelegt: docs/observability/redis-stores.md, docs/database/connection-pooling.md, docs/database/read-replicas.md, docs/database/outbox-pattern.md
  • gsd-code-reviewer-Subagent hat alle 12 Tasks-Commits reviewed; Findings adressiert
  • Phase-Summary in dieser README.md ergänzt (siehe unten)
  • Rollback-Drill durchgeführt: PgBouncer-Container down → Web läuft via DIRECT_URL-Fallback weiter; Redis down → Memory-Adapter-Fallback aktiv (Single-Instance-Mode)

Phase Summary

(Wird nach Abschluss der Phase vom ausführenden Agent ergänzt.)

Lessons Learned

(Wird nach Abschluss ergänzt.)

Verweise

  • Detail-MD-Template: ../DETAIL-MD-TEMPLATE.md
  • Agent-Workflow: ../AGENT-INSTRUCTIONS.md
  • Execution-Order: ../00-execution-order.md
  • Live-Status: ../PROGRESS.md
  • Vorgänger-Phase (Observability): ../02-observability/README.md
  • Nachfolge-Phase (Search + RAG): ../04-search-rag/README.md