Microservice unter Last stabilisiert
PgBouncer & Import‑Optimierung sichern zuverlässige Verfügbarkeit trotz wachsender Datenmengen.
Ausgangslage
Ein datenintensiver Go‑Microservice erzeugte unter Last zu viele Datenbankverbindungen. Die Folge waren Verbindungsabbrüche und gelegentliche Timeouts im Produktivbetrieb – insbesondere bei parallelen Importen und Backfill‑Jobs.
Das kritische Symptom war nicht nur ein einzelner Timeout, sondern die Kettenreaktion: wenn Verbindungen eskalieren, kippt schnell mehr als nur ein Job.
Zielbild & Anforderungen
Im Fokus stand nicht „mehr Leistung um jeden Preis“, sondern Stabilität unter realer Produktionslast: kontrollierte Verbindungen, planbare Laufzeiten und nachvollziehbare Ursachenanalyse.
- Zuverlässige Verfügbarkeit auch bei parallelen Importen und Backfill-Jobs
- Verbindungs- und Laststeuerung: DB-Limits einhalten, ohne Durchsatz zu verlieren
- Planbare Importlaufzeiten statt sporadischer Timeouts
- Messbarkeit als Standard: Dashboards, Alarme und klare SLO-nahe Indikatoren
Rahmenbedingungen: produktionsnaher Betrieb, wachsende Datenmengen, parallele Jobs, harte DB-Connection-Limits, Änderungen müssen rückrollbar sein.
Lösungsweg
Der Kern war Entkopplung und Steuerung: Imports wurden entzerrt, die Datenbankverbindungen über einen Pool kanalisiert und das Verhalten über Metriken sichtbar gemacht. Dadurch ließ sich Last nicht nur aushalten, sondern aktiv steuern.
- Baseline schaffen: Peak-Zeiten und Flaschenhälse messbar machen
- Importstrategie entzerren (Batching, Job-Entkopplung, Backpressure)
- Verbindungsmanagement über PgBouncer (Pool statt unkontrollierter Spikes)
- Konkrete Limits & Alarme definieren (Connections, Fehlerquoten, Laufzeiten)
- Sicherer Rollout über Feature-Toggle, Shadow-Run und klare Umschaltpunkte
Umsetzung
Die Umsetzung kombinerte Maßnahmen auf zwei Ebenen: Anwendung (Importlogik) und Datenbankzugriff (Pooling). So konnte der Dienst unter Last stabil bleiben, ohne dass Einzelspitzen die gesamte Umgebung mitreißen.
Wir haben die Importstrategie entzerrt (Batching & Backpressure), Connection Pooling über PgBouncer eingeführt und Hot‑Paths in SQL überarbeitet. Zusätzlich wurden worker‑seitige Concurrency‑Limits gesetzt und die Telemetrie ausgebaut, um Kapazitäten sichtbar zu machen.
Konkret (vereinfacht)
# pgbouncer.ini
[databases]
appdb = host=postgres port=5432 dbname=appdb
[pgbouncer]
listen_port = 6432
pool_mode = transaction
max_client_conn = 200
default_pool_size = 20
server_reset_query = DISCARD ALL
// Go (database/sql) – DSN zeigt auf PgBouncer bei Port 6432
sql.Open("pgx", os.Getenv("DATABASE_URL"))
// Poolgrenzen an die DB‑Kapazität anpassen
DB.SetMaxOpenConns(20)
DB.SetMaxIdleConns(20)
DB.SetConnMaxLifetime(5 * time.Minute)
Ziel: konstante Last auf der Datenbank, kurze Transaktionen, vorhersehbare Parallelität – statt ungebremster Verbindungsflut.
Ergebnis / Impact
- Importlaufzeiten stabilisiert, keine Timeouts unter Produktionslast
- Datenbank‑Verbindungslimits eingehalten (Pool‑gesteuert)
- Deutlich robustere Verfügbarkeit des Dienstes
Messung über Laufzeit‑Logs & DB‑Metriken (z. B. aktive Verbindungen, Fehlerquoten).
Validierung & Qualität
Stabilität ist nur so gut wie die Messung dahinter. Deshalb war der wichtigste Schritt: erst eine belastbare Baseline, dann iterativ optimieren und jede Änderung mit Daten abzusichern.
- Baseline über
pg_stat_activity& Logs (Peak‑Verbindungen, Wartesituationen) - Vergleich vor/nach Pooling (Timeouts, Fehlerquoten, Importdauer)
- Dashboards & Alarme für Verbindungsauslastung und Fehler
Zusätzlich haben wir typische Fehlerbilder (z. B. Connection-Spikes, lange Locks, Timeouts) als konkrete Signale im Monitoring verankert. Damit wird „gefühlt stabil“ zu „nachweisbar stabil“.
Betrieb & Rollout
Im Betrieb zählt vor allem, wie kontrolliert Änderungen live gehen. Hier war die Leitlinie: jede Maßnahme muss einzeln aktivierbar sein, und jede Aktivierung muss beobachtbar sein.
- Feature‑Toggle & Shadow‑Run der neuen Importpfade vor Umschaltung
- Readiness/Liveness‑Probes auf Downstream‑Abhängigkeiten
- Rollback‑Pfad pro Umgebung (sichere Umschaltpunkte)
Das Ergebnis ist ein Setup, das auch bei wachsender Last nicht plötzlich kippt. Wenn Last steigt, steigen Metriken. Nicht das Risiko.
Was heute anders ist
Die Importprozesse laufen planbar durch, die Verbindungen bleiben unterhalb der erlaubten Limits, und der Dienst ist unter Last deutlich robuster. Erweiterungen können schrittweise aktiviert und über Metriken überwacht werden.