Saga-Muster und verteilte Transaktionen
Saga-Muster und verteilte Transaktionen
1) Warum brauchen wir Sagas
Die klassische 2PC (Zwei-Phasen-Fixierung) ist schlecht skalierbar, kompliziert unter Ausfällen und blockiert Ressourcen. Die Saga unterteilt den gesamten Geschäftsprozess in eine Abfolge lokaler Transaktionen (Schritte), die jeweils unabhängig voneinander ausgeführt werden. Im Falle eines Fehlers werden die nachfolgenden Schritte abgebrochen und die bereits durchgeführten - werden durch umgekehrte Operationen kompensiert.
Das Ergebnis: eine überschaubare Ereigniskonsistenz ohne globalen Lockdown, eine hohe Überlebensfähigkeit und ein klares Wiederherstellungsprotokoll.
2) Basismodelle
2. 1 Orchestrierung
Ein dedizierter Saga-Koordinator verwaltet die Schritte: sendet Befehle, wartet auf Antworten/Ereignisse, initiiert Kompensationen.
Vorteile: zentrale Kontrolle, einfache Beobachtbarkeit, klare Fristen. Nachteile: zusätzliche Komponente.
2. 2 Choreographie
Kein Koordinator; Die Dienste reagieren auf die Ereignisse des anderen („OrderPlaced“ → „PaymentCaptured“ → „InventoryReserved“...).
Vorteile: schwache Konnektivität. Nachteile: schwieriger zu verfolgen, das Risiko eines „Todestanzes“ ohne klare Regeln.
2. 3 TCC (Try-Confirm/Cancel)
Option mit „Einfrieren“ von Ressourcen:1. Try - Vorbereitung/Reserve,
2. Confirm - Fixierung,
3. Abbrechen - Rollback.
Die Garantien sind höher, aber die Verträge und Zeiträume der Reserven sind komplizierter.
3) Stufen- und Vergütungsverträge
Jeder Schritt = lokale Transaktion + Kompensation (idempotent, erlaubt Wiederholung).
Die Entschädigung ist nicht zur vollständigen „Rückgabe der Welt“ verpflichtet - eine Domänenäquivalenz reicht aus (z.B. „Rückzahlungszahlung“ statt „Zahlung löschen“).
Identifizieren Sie Invarianten: für Geld - das Gleichgewicht geht nicht ins Minus; für Bestellungen - keine „hängenden“ Status.
Geben Sie Deadlines/TTL-Reserven und einen „Garbage Collector“ für überfällige Versuche ein.
4) Konsistenz und Semantik der Lieferung
Zustellung von Nachrichten: at-least-once (Standard) → alle Transaktionen müssen idempotent sein.
Reihenfolge: Wichtig durch den Korrelationsschlüssel (z.B. 'order _ id', 'player _ id').
Exactly-once ist nicht das Ziel der Saga; Wir erreichen eine effektive Just-in-One-Genauigkeit durch idempotente Schlüssel, Outbox/Inbox und korrekte Commitation.
5) Der Zustand der Saga und ihr Log
Was zu lagern:- „saga _ id“, „correlation _ id“, aktueller Status (Running/Completed/Compensating/Compensated/Failed),
- Schritt und seine Variablen (IDs der Zahlungen/Reserven),
- Historie (Log) von Ereignissen/Entscheidungen, Zeitstempel, Deadlines, Anzahl der Retrays.
- Ein separater Saga Store (Tabelle/Dokument), der dem Koordinator zur Verfügung steht.
- Für die Choreografie sind es lokale „Agenten“ der Saga, die Statusereignisse in ein gemeinsames Topic veröffentlichen.
6) Zuverlässige Veröffentlichungsmuster: outbox/inbox
Outbox: Schritt, um Änderungen zu initiieren und das Ereignis/den Befehl in der Outbox-Tabelle in einer einzigen Transaktion zu schreiben; worker postet im Reifen.
Inbox: Der Verbraucher führt eine Tabelle der verarbeiteten 'message _ id' → dedup + idempotency.
Nach der erfolgreichen Nebenwirkung des commitime offset/ACK (Kafka/RabbitMQ) - nicht früher.
7) Entwerfen von Saga-Schritten
7. 1 Beispiel (Kauf bei iGaming/E-Commerce)
1. PlaceOrder → den Status „PENDING“.
2. AuthorizePayment (Try) → `payment_hold_id`.
3. ReserveInventory → `reservation_id`.
4. CapturePayment (Confirm).
5. FinalizeOrder → `COMPLETED`.
- wenn (3) die → "CancelPaymentHold' fehlgeschlagen ist;
- wenn (4) nach (3) → 'ReleaseInventory' fehlgeschlagen ist;
- wenn (5) die → 'RefundPayment' und 'ReleaseInventory' fehlgeschlagen ist.
7. 2 Deadlines/Retrays
Maximal N Retrays mit exponentieller Verzögerung + Jitter.
Nach Überschreitung - Übergang zu „Compensating“.
Bewahren Sie next_attempt_at und deadline_at für jeden Schritt auf.
8) Orchestrator vs Plattform
Die Optionen sind:- Easy Home Orchestrator (Microservice + Saga Tabelle).
- Plattformen: Temporal/Cadence, Camunda, Netflix Conductor, Zeebe - geben Timer, Retrays, langlebige Workflows, Sichtbarkeit und eine Web-Konsole.
- Verwenden Sie für die Choreographie einen Veranstaltungskatalog und eine strenge Status-/Schlüsselvereinbarung.
9) Integrationsprotokolle
9. 1 Asynchron (Kafka/RabbitMQ)
Befehle: 'Zahlungen. authorize. v1`, `inventory. reserve. v1`.
Ereignisse: 'Zahlungen. authorized. v1`, `inventory. reserved. v1`, `payments. captured. v1`, `payments. refunded. v1`.
Partienschlüssel = 'order _ id '/' player _ id' für die Reihenfolge.
9. 2 Synchron (HTTP/gRPC) innerhalb eines Schritts
Zulässig für „kurze“ Schritte, aber immer mit Timeouts/Retrays/Idempotenz und Fallback in asynchroner Kompensation.
10) Idempotenz und Schlüssel
Bei Befehls- und Ausgleichsanfragen 'idempotency _ key' übergeben.
Nebenwirkungen (DB-Eintrag/Abschreibung) werden bedingt durchgeführt: „ausführen, wenn noch nicht gesehen 'idempotency _ key'“.
Auch die Entschädigungen sind idempotent: Eine Wiederholung von 'RefundPayment (id = X)' ist sicher.
11) Fehlerbehandlung
Klassen:- Transient (Netzwerke/Timeouts) → Retrays/Backoff.
- Geschäft (unzureichende Mittel, Grenzen) → sofortige Entschädigung/alternativer Weg.
- Irrecoverable (Invarianzverletzung) → manueller Eingriff, „manuelle“ Kompensation.
- Erstellen Sie eine Entscheidungsmatrix: Fehlerart → Aktion (retry/compensate/escalate).
12) Beobachtbarkeit und SLO sagen
SLI/SLO:- Ende-zu-Ende-Latenz der Saga (p50/p95/p99).
- Erfolgsrate (Anteil der abgeschlossenen ohne Entschädigung).
- Mean time to compensate и compensation rate.
- Orphaned sagas (hängend) und die Zeit bis GC.
- Trace: 'trace _ id '/' saga _ id' als Span-Link zwischen den Schritten; Burn-Rate-Metriken für Fehlerbudgets.
Protokolle: jede Änderung des Saga-Status = strukturierter Eintrag mit Ursache.
13) Beispiele (Pseudocode)
13. 1 Orchestrator (Idee)
python def handle(OrderPlaced e):
saga = Saga. start(e. order_id)
saga. run(step=authorize_payment, compensate=cancel_payment)
saga. run(step=reserve_inventory, compensate=release_inventory)
saga. run(step=capture_payment, compensate=refund_payment)
saga. run(step=finalize_order, compensate=refund_and_release)
saga. complete()
def run(step, compensate):
try:
step () # local transaction + outbox except Transient:
schedule_retry()
except Business as err:
start_compensation(err)
13. 2 Outbox (Tischidee)
outbox(id PK, aggregate_id, event_type, payload, created_at, sent_at NULL)
inbox(message_id PK, processed_at, status)
saga(order_id PK, state, step, next_attempt_at, deadline_at, context JSONB)
saga_log(id PK, order_id, time, event, details)
13. 3 Choreographie (Ideen der Themen)
`orders. placed '→ Verbraucher: ' Zahlungen. authorize`, `inventory. reserve`
`payments. authorized` + `inventory. reserved` → `orders. try_finalize`
Jede Ablehnung → 'orders. compensate' → initiiert 'Zahlungen. cancel/refund`, `inventory. release`.
14) Vergleich mit 2PC und ES
2PC: starke Konsistenz, aber Blockaden, Engpässe, „Kupferrohre“.
Saga: eventual consistency, wir brauchen die Disziplin der Kompensation und Telemetrie.
Event Sourcing: speichert Ereignisse als Quelle der Wahrheit; Die Sagas darauf sind natürlich, fügen aber die Komplexität der Migrationen/Snap-Shots hinzu.
15) Sicherheit und Compliance
Transportsicherheit (TLS/mTLS), ACL per topic/queue.
In den Ereignissen - Minimum PII, Verschlüsselung empfindlicher Felder, Tokenisierung.
Prüfung des Zugangs zu Sagas und Vergütungsprotokollen.
SLA mit externen Anbietern (Zahlungen/Versand) = Parameter für Fristen und Retraylimits.
16) Implementierung Checkliste (0-45 Tage)
0-10 Tage
Hervorhebung der Kandidatenprozesse (Multiservice, mit Kompensation).
Wählen Sie das Modell (Orchestrierung/Choreographie/TCC) und den Korrelationsschlüssel.
Beschreiben Sie die Schritte/Kompensationen, Invarianten und Deadlines. Heben Sie die Tabellen 'saga', 'outbox', 'inbox' an.
11-25 Tage
Aktivieren Sie Outbox/Inbox, Idempotenz und Backoff-Retrays.
Deployte die ersten Saga; SLI/SLO Dashboards und Trace hinzufügen.
Schreiben Sie ein Runbook von Entschädigungen (einschließlich manueller) und Eskalationen.
26-45 Tage
Automatisieren Sie GCs von „hängenden“ Sagen, periodische Neustarts/Fristfortsetzungen.
Verbringen Sie einen Spieltag: Schrittversagen, Überschreitung der Frist, Nichtverfügbarkeit des Brokers.
Standardisieren Sie Veranstaltungsverträge (Versionen, Kompatibilität), erstellen Sie ein „Saga-Verzeichnis“.
17) Anti-Muster
„Compensation = delete from DB“ statt domänenrichtiger Rückwirkung.
Keine Outbox/Inbox → Ereignisverlust/Doppeleffekte.
Retrays ohne Jitter → Selbst-DDoS-Abhängigkeiten.
Erwartung einer starken Konsistenz beim Lesen ohne „Verarbeitung im Gange“....
Ein gigantischer Orchestrator für alle → Kontrollmonolithen.
Totale Choreographie ohne Sichtbarkeit und SLA → unkontrollierbarer Tanz.
Das Ignorieren von Terminen → ewige Reserven/Holds.
18) Reifegradkennzahlen
≥ 90% der kritischen Prozesse werden durch Sagas/Kompensationen abgedeckt und haben die beschriebenen Invarianten.
Outbox/inbox sind für alle Tier-0/1-Produzenten/-Konsumenten integriert.
SLO: p95 Ende-zu-Ende-Saga normal, Erfolgsrate stabil, orphaned <Ziel.
Transparentes Tracing und Dashboards „in Schritten“, Burn-Rate-Alerts.
Vierteljährlicher Spieltag und Überprüfung der manuellen Runbook-Entschädigung.
19) Schlussfolgerung
Die Saga ist ein praktischer Konsistenzvertrag für verteilte Systeme: klare Schritte und Rückschritte, Publishing-Disziplin (Outbox/Inbox), Deadlines und Retrays, Beobachtbarkeit und Kompensationsprozesse. Wählen Sie ein Modell (Orchestrierung/Choreografie/TCC), fixieren Sie Invarianten und Schlüssel, machen Sie Handler idempotent - und Ihre Multi-Service-Geschäftsprozesse werden ohne teure 2PC vorhersehbar und nachhaltig.