Wenn ein Kafka-Consumer einen Nebeneffekt auslöst, gehen Sie davon aus, dass Duplikate möglich sind, bis Sie das Gegenteil bewiesen haben.
Das ist die sicherere Standardvorgehensweise.
Kafka hat Genau-einmal-Funktionen, aber Ingenieure interpretieren oft über, was diese Garantien abdecken. Sie helfen viel im eigenen Verarbeitungsmodell von Kafka. Sie machen jedoch nicht magisch jeden nachgelagerten Datenbankschreibvorgang oder externen API-Aufruf duplikatfrei.
Das Verbraucherproblem
Das riskante Muster sieht so aus:
- Nachricht konsumieren
- Datenbank aktualisieren
- Fortschritt bestätigen
Wenn der Dienst zwischen den Schritten abstürzt, kann ein Replay stattfinden.
Das ist nicht Kafka, das kaputt ist. Es ist normales Verhalten verteilter Systeme unter Fehlerbedingungen.
Verwenden Sie einen Idempotenzschlüssel
Jedes Ereignis, das einen Nebeneffekt auslösen kann, sollte einen stabilen Identifikator tragen:
{
"eventId": "evt_01HS8T0X8Q5Q8X4X8H2K2YJ6FD",
"type": "PaymentCharged",
"walletId": "wal_123"
}
Machen Sie dann den Nebeneffekt davon abhängig, ob dieses Ereignis bereits angewendet wurde.
Lassen Sie die Datenbank es durchsetzen
Ein gängiger und zuverlässiger Ansatz ist ein eindeutiger Schlüssel:
CREATE TABLE processed_events (
event_id text PRIMARY KEY,
processed_at timestamptz NOT NULL DEFAULT now()
);
Verarbeiten Sie dann innerhalb einer Transaktion:
BEGIN;
INSERT INTO processed_events (event_id)
VALUES ($1);
UPDATE wallets
SET balance = balance - $2
WHERE id = $3;
COMMIT;
Wenn dasselbe Ereignis erneut abgespielt wird, schlägt das Einfügen am eindeutigen Schlüssel fehl, und der Verbraucher kann es als bereits bearbeitet behandeln.
Das ist oft einfacher und vertrauenswürdiger als ad-hoc Deduplication im Speicher.
Das nützliche mentale Modell
Zielen Sie nicht darauf ab, dass "Duplikate niemals passieren."
Zielen Sie darauf ab, dass "Duplikate den finalen Zustand nicht ändern."
Das ist es, was Idempotenz Ihnen bringt.
Weiterführende Literatur