- Oban.py ist eine auf PostgreSQL basierende Portierung von Elixirs Job-Verarbeitungs-Framework Oban nach Python, mit der Jobs allein über die Datenbank eingefügt und verarbeitet werden können
- Jobs werden innerhalb von Datenbanktransaktionen erstellt und zurückgerollt; unterstützt werden unter anderem Queue-Management, Ergebnisspeicherung und Cron-Scheduling
- Die Open-Source-Version hat Einschränkungen wie Single-Thread-
asyncio-Ausführung und individuelles Einfügen und Bestätigen, während die Pro-Version Parallelverarbeitung, Workflows und Smart Concurrency bietet
- Der interne Ablauf besteht aus fünf Schritten:
Insert → Notify → Fetch → Execute → Ack; dabei wird PostgreSQLs FOR UPDATE SKIP LOCKED genutzt, um Konflikte bei der Parallelität zu vermeiden
- Leader Election, Wiederherstellung verwaister Jobs und Backoff-Retries werden ebenfalls datenbankbasiert ausgeführt und ermöglichen so eine stabile verteilte Verarbeitung ohne externen Broker
Überblick über Oban.py
- Oban.py ist eine nach Python portierte, datenbankbasierte Job-Verarbeitungs-Framework-Variante von Elixirs Oban
- Jobs werden innerhalb von Datenbanktransaktionen eingefügt und verarbeitet; bei Fehlern wird die gesamte Transaktion zurückgerollt
- Enthält verschiedene Steuerungsfunktionen wie Queue-Limits, Speicherung abgeschlossener Jobs, Ergebnisaufbewahrung und Cron-Scheduling
- Es gibt zwei Versionen
- Open Source (OSS): Single-Thread-
asyncio-Ausführung, individuelles Einfügen und Bestätigen, einfache Wiederherstellung
- Pro-Version: Parallelverarbeitung auf Basis eines Prozesspools, Unterstützung für Workflows, Relays, Unique Jobs und Smart Concurrency
- OSS eignet sich für persönliche Projekte oder Evaluierungen, für große Umgebungen wird die Pro-Version empfohlen
Pfad der Job-Verarbeitung
- Nach dem Einfügen werden Jobs in der Tabelle
oban_jobs mit state='available' gespeichert, und per PostgreSQL-NOTIFY wird eine Benachrichtigung an jeden Knoten gesendet
- Der Stager jedes Knotens erkennt die betreffende Queue und weckt den Producer auf, der die Jobs abholt und ausführt
- Bei der Job-Auswahl wird SQLs
FOR UPDATE SKIP LOCKED verwendet, um parallele Verarbeitung ohne doppelte Ausführung zu ermöglichen
- Bereits gesperrte Zeilen werden übersprungen, sodass andere Producer sofort andere Jobs holen können
- Jobs werden als Async-Tasks dispatcht; nach Abschluss erfolgt die Acknowledgement-Verarbeitung per Callback
- Die Pro-Version verwendet statt
asyncio einen Prozesspool-Dispatcher und unterstützt so parallele Ausführung über mehrere CPU-Kerne
Hintergrundprozesse
- Leader Election
- Der Leader wird über PostgreSQLs
INSERT ... ON CONFLICT und ein lease-basiertes TTL-Verfahren bestimmt
- Ohne separates Konsensprotokoll übernimmt ein einzelner Leader das Aufräumen und Wiederherstellen von Jobs
- Lifeline (Wiederherstellung verwaister Jobs)
- Läuft ein Job länger als eine bestimmte Zeit (
rescue_after, standardmäßig 5 Minuten), wird er in den Status available zurückversetzt
- Die Pro-Version prüft, ob der Producer noch lebt, OSS entscheidet nur zeitbasiert
- Pruner (Job-Bereinigung)
- Löscht abgeschlossene, abgebrochene oder verworfene Jobs, deren
max_age (standardmäßig 1 Tag) überschritten ist
- Begrenzt den Löschumfang mit
LIMIT, um Datenbanklast zu vermeiden
Retries und Backoff
- Wenn ein Job eine Ausnahme auslöst, entscheidet der Executor, ob ein Retry erfolgt
- Liegt die Zahl unter der maximalen Anzahl an Versuchen (
max_attempts), wird erneut versucht, andernfalls wird der Job verworfen
- Der Standard-Backoff ist ein exponentieller Anstieg mit Jitter
- Verhindert bei vielen Fehlschlägen gleichzeitige Retries und mildert so Lastspitzen durch den Thundering Herd
- Beispiel: ca. 17 Sekunden beim 1. Versuch, ca. 47 Sekunden beim 5. Versuch, ca. 17 Minuten beim 10. Versuch
- Worker-Klassen können über die Methode
backoff() eine benutzerdefinierte Backoff-Logik implementieren
Wichtige Merkmale und Bewertung
- PostgreSQL übernimmt die Schlüsselrolle
- Über
FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY und ON CONFLICT werden Parallelitätskontrolle, Signalisierung und Leader Election vollständig umgesetzt
- Statt Redis oder eines externen Brokers wird eine einzelne Datenbank als Koordinationsschicht genutzt
- Nicht parallel, aber nebenläufig
- Auf
asyncio basierend und damit gut für I/O-bound Workloads geeignet; für CPU-bound Aufgaben wird die Pro-Version empfohlen
- Klare Code-Struktur
- Konsistente Benennung und sauber getrennte Verantwortlichkeiten sorgen für eine gut lesbare Codebasis
- Klare Rollentrennung zwischen OSS und Pro
- OSS für Experimente und kleinere Einsätze, Pro für große Umgebungen und hohe Performance
- Fazit: Eine saubere und strukturiert umgesetzte Python-Portierung, die allein mit PostgreSQL eine vollständige Job-Queue implementiert, und geeignet ist für Elixir-Nutzer oder Entwickler, die ein Job-System ohne externe Infrastruktur suchen
Noch keine Kommentare.