Hoe je PostgreSQL achtergrondtaken laat voltooien, zelfs na een herstart
Stel je dit scenario voor: je schrijft een complexe dataverwerkingsworkflow. Je moet records ophalen, ze naar een externe API sturen, de status in de database bijwerken en vervolgens de rapportgeneratie starten. Als de database "crashed" of de server halverwege het proces opnieuw opstart, zal een standaard PL/pgSQL procedure gewoon afbreken. Het resultaat is "loshangende" data en een hoofdpijn: hoe kom je erachter in welke fase alles is gestopt, en hoe start je het veilig opnieuw?
Meestal lossen we dit op met workarounds. We stellen pg_cron in, maken tabellen met jobwachtrijen, schrijven workers in Python of Go die constant de database pollen. In het ergste geval slepen we zware monsters zoals Temporal of Airflow het project in. Maar engineers bij Microsoft vonden al deze onnodige complexiteit en hebben de pg_durable extensie uitgebracht.
Waarom ontwikkelaars dit nodig hebben
Het hoofdidee achter het project is om PostgreSQL de mogelijkheid te geven om langlopende functies uit te voeren die bestand zijn tegen fouten. In de terminologie van de auteurs wordt dit "Durable Execution" genoemd.
Als je workflow is gedefinieerd via pg_durable, zal de database zijn status na elke stap opslaan. Als de server precies halverwege de uitvoering van een zware query of API-aanroep opnieuw opstart, zal de extensie de taak oppikken vanaf het laatste succesvolle checkpoint. Je hoeft niet langer cron-taken, workers en statustabellen aan elkaar te plakken.
Hoe het onder de motorkap werkt
Het project is geschreven in Rust met behulp van pgrx. Architecturally is het niet zomaar een wrapper maar een volwaardige uitvoeringsomgeving binnen de database. Het bestaat uit verschillende lagen:
- SQL DSL: een set operators voor het beschrijven van de taakgrafiek.
- Background Worker: een achtergrondproces binnen Postgres dat de uitvoering beheert.
- Duroxide: een orkestratie-engine (ook een Microsoft-ontwikkeling) die deterministische replay en checkpoints afhandelt.
Interessant is dat de auteurs een "SQL-native" benadering hebben gekozen. Je beschrijft de logica direct in de console of migratie, met behulp van speciale operators zoals ~> of |=>.
Belangrijkste functies
Hier zijn drie dingen die je leven echt vereenvoudigen.
Fouttolerantie zonder externe services
Je hebt geen Redis voor wachtrijen of aparte Temporal-instanties nodig. Alles leeft right in de df.* en duroxide.* tabellen. Data en controlelogica bevinden zich in dezelfde transactionele omgeving. Dit elimineert het klassieke gedistribueerde systeemprobleem waarbij een taak in de wachtrij bestaat maar wijzigingen in de database nog niet zijn gecommit.
Parallelle uitvoering en samenvoeging
Met operators kun je eenvoudig de taakuitvoering "forken" naar meerdere parallelle streams en vervolgens wachten tot ze zijn voltooid. De README heeft een duidelijk voorbeeld: tel gebruikers, bestellingen en omzet tegelijkertijd, en consolideer vervolgens alles in één rapportagestap.
Integratie met externe systemen
De extensie heeft een df.http() functie. Dit betekent dat je externe microservices of neurale netwerk-API's direct kunt aanroepen vanuit een langlopend proces. Als de API een 500-fout retourneert, kan pg_durable wachten en opnieuw proberen zonder de hele databasewerking te blokkeren.
Codevoorbeeld
Zo ziet het maken van een eenvoudige taak er direct in SQL uit:
-- Запускаем процесс: берем 100 необработанных документов и обновляем их статус
SELECT df.start(
'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);
Deze code maakt een Workflow-instantie die gegarandeerd zal worden uitgevoerd. Als de UPDATE mislukt, zal het systeem exact weten welke batch van data het probeerde te verwerken.
Waar dit van pas komt
Ik zie verschillende scenario's waarin pg_durable je veel tijd zal besparen.
Ten eerste, AI-pipelines. Als je duizenden rijen door embeddings moet halen en ze moet opslaan in pgvector, is dit het ideale hulpmiddel. Tekstchunking, OpenAI API-aanroepen en upserts naar de database zijn verpakt in één betrouwbare pipeline.
Ten tweede, grootschalige dataverwerking (ETL). In plaats van monsterlijke PL/pgSQL procedures te schrijven die omvallen wanneer de WAL opraakt, kun je het werk opsplitsen in kleine stappen met checkpoints.
Ten derde, beheerautomatisering. Bijvoorbeeld het controleren van tabelbloat, het verzenden van een melding en wachten op goedkeuring — dit kan allemaal worden beschreven als een Durable Function.
Nuances en beperkingen
Het project is in Preview-status. Dit betekent dat het te vroeg is om het naar productie te pushen, maar het is perfect voor interne tools.
Belangrijke beperkingen: je hebt PostgreSQL 17 of 18 nodig. Als je op oudere versies zit, moet je upgraden. Een ander punt is beveiliging. Standaard vereisen alle functies in shared_preload_libraries superuser-rechten voor setup, hoewel de ontwikkelaars een permissiesysteem via df.grant_usage() hebben geleverd voor reguliere rollen.
Het systeem is geoptimaliseerd voor SQL. Als je complexe bedrijfslogica met lastige loops in Python of Node.js nodig hebt, kun je beter de Duroxide-engine direct vanuit je applicatiecode gebruiken. pg_durable gaat specifiek over het zo dicht mogelijk bij de data houden van berekeningen.
Microsoft investeert actief in Postgres (denk aan de Citus-overneming), en pg_durable is een weitere stap om de database om te toveren tot een volwaardig applicatieplatform. Als je genoeg hebt van je achtergrondtaken die "verdwijnen" op de meest ongelegen momenten, of als je het beu bent om externe orkestratoren te configureren voor eenvoudige pipelines, bekijk dan zeker deze repository. Om te beginnen is een eenvoudige Docker-container of Codespaces voldoende, die al zijn geconfigureerd in het Development-tabblad in de repository.
Gerelateerde projecten