So bringen Sie PostgreSQL dazu, Hintergrundaufgaben auch nach einem Neustart abzuschließen
Stellen Sie sich folgendes Szenario vor: Sie schreiben einen komplexen Datenverarbeitungs-Workflow. Sie müssen Datensätze abrufen, an eine externe API senden, den Status in der Datenbank aktualisieren und dann die Berichtsgenerierung starten. Wenn die Datenbank „abstürzt" oder der Server mitten im Prozess neu startet, wird eine Standard-PL/pgSQL-Prozedur einfach abgebrochen. Das Ergebnis sind „verwaiste" Daten und ein großes Problem: Wie finden Sie heraus, an welcher Stelle alles gestoppt wurde, und wie starten Sie es sicher neu?
Normalerweise lösen wir dies mit Workarounds. Wir richten pg_cron ein, erstellen Tabellen mit Job-Warteschlangen, schreiben Worker in Python oder Go, die ständig die Datenbank abfragen. Im schlimmsten Fall schleppen wir schwere Geschütze wie Temporal oder Airflow in das Projekt ein. Aber Ingenieure bei Microsoft fanden, dass diese ganze Komplexität unnötig war, und veröffentlichten die pg_durable-Erweiterung.
Warum Entwickler dies benötigen
Die Hauptidee hinter dem Projekt ist, PostgreSQL die Fähigkeit zu geben, langlebige Funktionen auszuführen, die gegen Ausfälle resilient sind. In der Terminologie der Autoren wird dies als „Durable Execution" (dauerhafte Ausführung) bezeichnet.
Wenn Ihr Workflow durch pg_durable definiert ist, speichert die Datenbank ihren Zustand nach jedem Schritt. Wenn der Server mitten in der Ausführung einer schweren Abfrage oder eines API-Aufrufs neu startet, setzt die Erweiterung die Aufgabe an der letzten erfolgreichen Checkpoint-Stelle fort. Sie müssen nicht mehr cron-Jobs, Worker und Status-Tabellen zusammensetzen.
So funktioniert es unter der Haube
Das Projekt ist in Rust unter Verwendung von pgrx geschrieben. Architektonisch ist es nicht nur ein Wrapper, sondern eine vollwertige Ausführungsumgebung innerhalb der Datenbank. Es besteht aus mehreren Schichten:
- SQL DSL: eine Reihe von Operatoren zur Beschreibung des Aufgabengraphen.
- Background Worker: ein Hintergrundprozess innerhalb von Postgres, der die Ausführung verwaltet.
- Duroxide: eine Orchestrierungs-Engine (ebenfalls eine Microsoft-Entwicklung), die deterministisches Replay und Checkpoints handhabt.
Interessanterweise wählten die Autoren einen „SQL-nativen" Ansatz. Sie beschreiben die Logik direkt in der Konsole oder Migration unter Verwendung spezieller Operatoren wie ~> oder |=>.
Wichtigste Funktionen
Hier sind drei Dinge, die Ihr Leben wirklich vereinfachen.
Fehlertoleranz ohne externe Dienste
Sie brauchen kein Redis für Warteschlangen oder separate Temporal-Instanzen. Alles lebt direkt in den Tabellen df.* und duroxide.*. Daten und Steuerungslogik befinden sich in derselben transaktionalen Umgebung. Dies eliminiert das klassische Problem verteilter Systeme, bei dem eine Aufgabe in der Warteschlange existiert, aber Änderungen in der Datenbank noch nicht committed wurden.
Parallele Ausführung und Zusammenführung
Mit Operatoren können Sie die Aufgabenausführung einfach in mehrere parallele Streams „abzweigen" und dann auf deren Abschluss warten. Die README enthält ein klares Beispiel: Zählen Sie Benutzer, Bestellungen und Umsatz gleichzeitig und konsolidieren Sie dann alles in einem einzigen Berichtsschritt.
Integration mit externen Systemen
Die Erweiterung verfügt über eine Funktion df.http(). Das bedeutet, Sie können externe Microservices oder neuronale Netzwerk-APIs direkt aus einem langlebigen Prozess aufrufen. Wenn die API einen 500-Fehler zurückgibt, kann pg_durable warten und es erneut versuchen, ohne den gesamten Datenbankbetrieb zu blockieren.
Codebeispiel
So sieht das Erstellen einer einfachen Aufgabe direkt in SQL aus:
-- Запускаем процесс: берем 100 необработанных документов и обновляем их статус
SELECT df.start(
'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);
Dieser Code erstellt eine Workflow-Instanz, deren Ausführung garantiert ist. Wenn das UPDATE fehlschlägt, weiß das System genau, welchen batch von Daten es zu verarbeiten versuchte.
Wo dies nützlich ist
Ich sehe mehrere Szenarien, in denen pg_durable Ihnen viel Zeit sparen wird.
Erstens, KI-Pipelines. Wenn Sie Tausende von Zeilen durch Embeddings verarbeiten und in pgvector speichern müssen, ist dies das ideale Werkzeug. Text-Chunking, OpenAI-API-Aufrufe und Upserts in die Datenbank sind in einer einzigen zuverlässigen Pipeline verpackt.
Zweitens, Großflächige Datenverarbeitung (ETL). Anstatt monstrous PL/pgSQL-Prozeduren zu schreiben, die zusammenbrechen, wenn das WAL voll läuft, können Sie die Arbeit in kleine Schritte mit Checkpoints aufteilen.
Drittens, Administrationsautomatisierung. Zum Beispiel Überprüfung der Tabellenfragmentierung, Senden einer Benachrichtigung und Warten auf Genehmigung – all dies kann als Durable Function beschrieben werden.
Nuancen und Einschränkungen
Das Projekt befindet sich im Preview-Status. Das bedeutet, es ist noch zu früh, es in die Produktion zu bringen, aber es eignet sich perfekt für interne Werkzeuge.
Wichtige Einschränkungen: Sie benötigen PostgreSQL 17 oder 18. Wenn Sie auf älteren Versionen sind, müssen Sie ein Upgrade durchführen. Ein weiterer Punkt ist die Sicherheit. Standardmäßig erfordern alle Funktionen in shared_preload_libraries Superuser-Rechte für die Einrichtung, obwohl die Entwickler ein Berechtigungssystem über df.grant_usage() für reguläre Rollen bereitgestellt haben.
Das System ist für SQL optimiert. Wenn Sie komplexe Geschäftslogik mit kniffligen Schleifen in Python oder Node.js benötigen, ist es besser, die Duroxide-Engine direkt aus Ihrem Anwendungcode heraus zu verwenden. pg_durable geht es speziell darum, Berechnungen so nah wie möglich an den Daten zu halten.
Microsoft investiert aktiv in Postgres (denken Sie an die Übernahme von Citus), und pg_durable ist ein weiterer Schritt dahin, die Datenbank in eine vollwertige Anwendungsplattform zu verwandeln. Wenn Sie es leid sind, dass Ihre Hintergrundaufgaben im ungünstigsten Moment „verschwinden", oder wenn Sie es satt haben, externe Orchestrierer für einfache Pipelines zu konfigurieren, schauen Sie sich auf jeden Fall dieses Repository an. Um loszulegen, reicht ein einfacher Docker-Container oder Codespaces aus, die bereits in der Registerkarte Development im Repository konfiguriert sind.
Ähnliche Projekte