Microservice unter Last stabilisiert

PgBouncer & Import‑Optimierung sichern zuverlässige Verfügbarkeit trotz wachsender Datenmengen.

Microservice unter Last stabilisiert

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.

  1. Baseline schaffen: Peak-Zeiten und Flaschenhälse messbar machen
  2. Importstrategie entzerren (Batching, Job-Entkopplung, Backpressure)
  3. Verbindungsmanagement über PgBouncer (Pool statt unkontrollierter Spikes)
  4. Konkrete Limits & Alarme definieren (Connections, Fehlerquoten, Laufzeiten)
  5. 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.

Kurzinfo

Dauer

ca. 8 Wochen

Rolle

Go & DB‑Tuning · Einführung PgBouncer · Import‑Optimierung

Werkzeuge

Go, PostgreSQL, PgBouncer, AWS (Monitoring/Logs)