Playbook

Datenbankgestützte Jobs mit Leases (Datenbankgestuetzte Jobs Mit Leases)

Einen Lease per Conditional UPDATE erwerben, der Stuck-Watcher, der verwaiste Arbeit rettet, und warum ein Nack des Brokers allein nicht reicht.

Verteilte Payment Engine (Distributed Payment Engine)

Teil 13 von 22

Serie zur verteilten Payment-Architektur — die Lücke zwischen Capture und Complete.

Distributed payment engine architecture diagram

Der vorherige Teil hat den Retry-Algorithmus aufgebaut — dabei aber still angenommen, dass wir wissen, welcher einzelne Worker einen Job ausführt. Wenn mehrere Worker aus derselben Job-Tabelle ziehen, kann derselbe Zahlungsjob gleichzeitig von zwei Workern verarbeitet werden?

Message-Broker lösen das mit Visibility-Timeouts oder Ack/Nack. In einer datenbankgestützten Job-Queue — eine übliche Wahl in Payment-Systemen, da der Job-Status ohnehin schon in der Datenbank liegt — kommt die gleiche Garantie über das Lease-Muster.

Worker A                          Worker B
   │ SELECT ... FOR UPDATE?           │
   │ oder ein Conditional UPDATE       │
   ▼                                  ▼
  Versucht, den Job zu sperren   Versucht, den Job zu sperren
   → nur einer gewinnt

Wo die Begriffe zuerst auftauchen

📦 Lease
Ein zeitgestempelter Eintrag, der markiert, dass ein Worker einen Job für eine begrenzte Zeit 'besitzt'.

📦 Conditional UPDATE
Eine atomare SQL-Anweisung, die eine Zeile nur aktualisiert, wenn eine erwartete Bedingung (z. B. status = Pending) zutrifft.

📦 Lease-TTL
Die Obergrenze, wie lange ein Worker einen Job halten darf; danach wird der Job wieder beanspruchbar.

📦 Stuck Watcher
Ein Hintergrundprozess, der periodisch Jobs sucht, deren Lease abgelaufen ist, ohne dass sie abgeschlossen wurden, und sie zurückgibt.

Ein Lease ist kein Lock; stürzt der Prozess ab, der einen Lock hält, kann dieser ewig bestehen bleiben. Da ein Lease eine TTL hat, gibt selbst ein abgestürzter Worker den Job irgendwann automatisch frei.

Der atomare Weg, einen Lease zu erwerben

Erst zu lesen und dann zu schreiben (Read-then-Write) ist einer Race Condition ausgesetzt: Zwei Worker können dieselbe Zeile lesen, beide sehen sie als frei, beide versuchen sie zu übernehmen. Der richtige Weg fasst Lesen und Bedingung in eine einzige atomare Anweisung:

UPDATE payment_jobs
SET status = 'Processing',
    lease_owner = :workerId,
    lease_until = now() + interval '60 seconds'
WHERE id = :jobId
  AND (status = 'Pending' OR (status = 'Processing' AND lease_until < now()))
RETURNING id;

Liefert dieses UPDATE keine Zeile zurück, gehört der Job bereits einem anderen Worker mit gültigem Lease — dieser Worker geht still zum nächsten Job über. Kommt eine Zeile zurück, ist dieser Worker der alleinige Besitzer, bis der Lease abläuft.

Warum die Lease-TTL eine Heartbeat-Verlängerung braucht

Eine feste Lease-Dauer (z. B. 60 Sekunden) passt nicht zu jeder Operation. Ein Schritt, der legitim lange dauern kann (ein PSP-Aufruf, der unerwartet stockt), sollte den Lease per Heartbeat verlängern, bevor er abläuft:

Worker startet → lease_until = jetzt + 60s
  ... Arbeit läuft weiter ...
Worker sendet Heartbeat → lease_until = jetzt + 60s (verlängert)
  ... Arbeit abgeschlossen ...
Worker markiert status = Completed

Ein Worker, der keinen Heartbeat senden kann (abgestürzt, Verbindung verloren), kann den Lease nicht verlängern; er läuft ab, und der Job wird wieder beanspruchbar. Das macht Absturzwiederherstellung automatisch statt manuell.

Der Stuck Watcher: wer bemerkt einen abgelaufenen Lease

Ein abgelaufener Lease 'rettet sich nicht selbst' — irgendein Worker muss die Zeile erneut per SELECT abfragen. Ein periodischer Watcher-Prozess sucht nach Jobs, die über ihre Lease-Ablaufzeit hinaus in Processing festsitzen, und markiert sie oder setzt sie direkt zurück auf Pending.

Watcher (alle 30 Sekunden)
  SELECT id FROM payment_jobs
  WHERE status = 'Processing' AND lease_until < now()
  → diese Jobs werden als stuck markiert oder direkt auf Pending zurückgesetzt

Ohne Watcher kann ein von einem abgestürzten Worker verwaister Job für immer in Processing verbleiben — und niemand merkt, dass eine Zahlung nie wirklich abgeschlossen wurde.

Warum ein Nack des Brokers allein nicht reicht

In einem Message-Broker geht eine Nachricht zurück in die Queue, wenn ein Worker sie Nackt (oder ihr Visibility-Timeout abläuft). Das ähnelt einem DB-gestützten Lease stark — aber zwei Unterschiede sind wichtig: Erstens ist das eigene Sichtbarkeitsfenster des Brokers meist nicht mit Ihrem persistenten Job-Status-Datensatz synchronisiert (eine Nachricht kann verloren gehen oder doppelt zugestellt werden); zweitens sagt Nack nur 'lass diese Nachricht los' — es verfolgt nicht dauerhaft, welchen Schritt der Job erreicht hat oder wie oft er versucht wurde. Ein datenbankgestützter Lease hält Job-Status und Versuchsverlauf in derselben, abfragbaren Transaktionsgrenze.

Häufig verwechselte Unterschiede

❌ Lease = Lock
✓ Ein Lease hat eine TTL; ein Lock kann ewig bestehen, wenn der haltende Prozess abstürzt

❌ Read-then-Write reicht aus
✓ Read-then-Write ist einer Race Condition ausgesetzt; ein Conditional UPDATE muss atomar sein

❌ Nack ersetzt einen Lease vollständig
✓ Nack verwaltet Nachrichtensichtbarkeit; ein Lease hält Job-Status und Versuchsverlauf dauerhaft

DB-gestützter Lease vs. Broker-Visibility-Timeout

Kriterium Broker-Visibility-Timeout DB-gestützter Lease
Abfragbarkeit des Status begrenzt vollständig per SQL
Dauerhaftigkeit des Versuchsverlaufs abhängig vom Broker natürlich in derselben Zeile
Stuck Jobs erkennen indirekt direkte Abfrage

Checkliste beim Entwurf von Leases

  1. Ist das Erwerben eines Leases eine einzige atomare UPDATE ... WHERE-Anweisung oder Read-then-Write?
  2. Ist die Lease-TTL tatsächlich länger als die längste erwartete Operationsdauer?
  3. Gibt es einen Heartbeat-Mechanismus zur Verlängerung des Leases bei legitim lang laufenden Operationen?
  4. Läuft ein Stuck Watcher periodisch, oder kann ein abgelaufener Lease einen Job für immer in Processing festhalten?
  5. Speichert das Feld lease_owner, welche Worker-Instanz den Job hält, zur Diagnose?
  6. Wird die Versuchsanzahl bei jedem Lease-Erwerb erhöht und dauerhaft gespeichert?

Was bleiben soll

  1. Ein Lease ist kein Lock; es ist ein zeitlich begrenzter Besitzanspruch, der automatisch abläuft.
  2. Das Erwerben eines Leases erfordert ein atomares Conditional UPDATE; Read-then-Write ist einer Race Condition ausgesetzt.
  3. Lang laufende Operationen müssen den Lease per Heartbeat verlängern, sonst kann er vorzeitig ablaufen.
  4. Ein Stuck Watcher ist ein zwingender Hintergrundprozess, der von abgestürzten Workern verwaiste Jobs rettet.

Die Zuverlässigkeit einer Job-Queue zeigt sich nicht auf dem Happy Path — sie zeigt sich in dem Moment, in dem ein Worker mitten im Job abstürzt.

Der nächste Teil betrachtet einen Worker, der genau auf diesem Lease-Mechanismus aufbaut: den Abgleich-Worker, der Zahlungen heilt, die der PSP als erfolgreich meldet, während das System sie noch als ausstehend zeigt.

FAQ

Häufige Fragen

Was ist Lease?

Ein zeitgestempelter Eintrag, der markiert, dass ein Worker einen Job für eine begrenzte Zeit 'besitzt'.

Was ist Conditional UPDATE?

Eine atomare SQL-Anweisung, die eine Zeile nur aktualisiert, wenn eine erwartete Bedingung (z. B. status = Pending) zutrifft.

Stimmt es, dass „Lease = Lock“?

Ein Lease hat eine TTL; ein Lock kann ewig bestehen, wenn der haltende Prozess abstürzt

Was legt dieser Teil fest?

Message-Broker lösen das mit Visibility-Timeouts oder Ack/Nack. In einer datenbankgestützten Job-Queue — eine übliche Wahl in Payment-Systemen, da der Job-Status ohnehin schon in der Datenbank liegt — kommt die gleiche Garantie über das **Lease**-Muster. Ein Lease ist kein Lock; es ist ein zeitlich begrenzter Besitzanspruch, der automatisch abläuft. Der vorherige Teil hat den Retry-Algorithmus aufgebaut — dabei aber still angenommen, dass wir wissen, welcher einzelne Worker einen Job ausführt. Wenn mehrere Worker aus derselben Job-Tabelle ziehen, kann derselbe Zahlungsjob gleichzeitig von zwei Workern verarbeitet werden?

Gelernte Engineering-Prinzipien

  • Ein Lease ist kein Lock; er ist zeitlich begrenzt und läuft automatisch ab.
  • Das Erwerben eines Leases braucht ein atomares Conditional UPDATE, kein Read-then-Write.
  • Ohne Stuck Watcher kann der Job eines abgestürzten Workers dauerhaft verloren gehen.

Weiterlesen

Weiterlesen

Nachster Teil der Serie

Nachster Teil der Serie

Aus derselben Serie

Paylaş