Sie brauchen kein weiteres verteiltes System, wenn Ihre Datenbank den Job erledigen kann. Aber Sie brauchen auch keine Datenbank, die alles übernimmt. Microsofts Entscheidung, eine in‑der‑Datenbank laufende Engine für dauerhafte Ausführung in Postgres (pg_durable) als Open Source bereitzustellen, legt diese Spannung heute auf Ihren Schreibtisch. Das Versprechen ist verführerisch: weniger bewegliche Teile, transaktionale Konsistenz und SQL‑native Beobachtbarkeit. Das Risiko ist ebenso real: größere Fehlerdomänen, schreibverstärkte Hot‑Tabellen und DBA‑Arbeit, die als Einfachheit daherkommt.
Dieser Beitrag liefert einen pragmatischen Entscheidungsrahmen. Wann sollten Sie Workflows nach Postgres verlagern? Wann sollten Sie bei einem externen Orchestrator wie Temporal, Cadence, Conductor, Argo oder AWS Step Functions bleiben? Und wenn Sie sich für in‑DB entscheiden, wie verhindern Sie, dass daraus Ihre nächste Incident‑Story wird?
Das Problem, das pg_durable lösen will
Dauerhafte Workflows brauchen drei Dinge:
- Zustand, der Prozess‑ und Maschinenneustarts übersteht.
- Timer und Retries mit Backoff, die bei Deployments nicht verschwinden.
- Exactly‑once oder effekt‑idempotente Ausführung, gekoppelt an Business‑Writes.
Die meisten Teams bauen sich das mit einer Queue, einer Job‑Tabelle und einigen „Best‑Effort“‑Idempotenzschlüsseln zusammen. Das funktioniert – bis es das nicht mehr tut. Externe Orchestratoren beheben das, fügen aber ein neues verteiltes System hinzu, das betrieben, erlernt und bezahlt werden will. In‑Database Durable Execution sagt: Lassen Sie Postgres den Workflow‑Zustandsautomaten und die Timer halten, und lassen Sie Ihre Worker Schritte mit transaktionaler Semantik pullen. Sie reduzieren Netzwerkrundreisen und hängen Business‑Writes und Workflow‑Übergänge in einem Commit zusammen.
Erste Prinzipien: die vier Achsen der Entscheidung
1) Workload‑Form
Profilieren Sie, was tatsächlich durch Ihre Workflows läuft:
- Durchsatz: durchschnittlich und p95 gestartete Workflows pro Sekunde; ausgeführte Schritte pro Sekunde.
- Schrittdauer: CPU‑gebunden in Ihrem Code (10–500 ms), I/O‑Wartezeiten zu Drittanbieter‑APIs (100 ms–10 s) oder Human‑in‑the‑Loop (Minuten–Tage).
- Fan‑out: Ein Workflow → N parallele Schritte? Wie groß kann N werden?
- Payload‑Größe: pro Übergang persistierte Schritt‑Ein-/Ausgaben (Bytes vs KB vs MB).
Warum das zählt: Postgres ist beeindruckend gut bei Tausenden kleiner Transaktionen pro Sekunde mit vorhersehbaren Zeilengrößen. Weniger glücklich ist es mit hochdrehenden Hot‑Partitionen, unbegrenzten Append‑Only‑Logs im Hauptcluster oder Payloads im Megabyte‑Bereich in der Workflow‑Historie. Als Plausibilitätscheck: Gut getunte Queue‑auf‑Postgres‑Systeme (z. B. pg_boss, pgbmq, SKIP LOCKED‑Patterns) halten auf einer einzelnen ordentlichen Maschine mit SSDs typischerweise 5–20k Jobs/s aus. Mit Partitionierung und aggressivem VACUUM geht mehr – aber dann sind Sie Queue‑Engineer. Externe Orchestratoren schieben darüber hinaus, ohne Ihre primäre Datenbank unter Druck zu setzen.
2) Daten‑Nähe und transaktionale Kopplung
Ändern Ihre Workflow‑Schritte Zeilen in derselben Postgres‑Datenbank, die Ihre Domain‑Wahrheit hält? Wenn ja, ist in‑DB‑Orchestrierung sehr attraktiv. Sie können Business‑Write und Workflow‑Übergang in derselben Transaktion ausführen. Das eliminiert Outbox/Relay‑Komplexität und schließt eine ganze Klasse von Race‑Windows. Rufen die meisten Schritte externe APIs oder andere Datenspeicher auf, sinkt der Kopplungsnutzen – und Sie belasten Postgres mit Zustand, der nicht dort liegt, wo die eigentliche Arbeit passiert.
3) Fehlerdomänen und Auswirkungsradius
Die Orchestrierung in Postgres vergrößert den Auswirkungsradius eines DB‑Incidents. Ein WAL‑Schub durch einen falsch konfigurierten Retry‑Sturm kann denselben Cluster degradieren, auf den Ihre Produkt‑Queries angewiesen sind. Wenn Ihre RTO/RPO‑Haltung den Primary bereits als Kronjuwel behandelt, wollen Sie dort wirklich Workflow‑Stürme wohnen lassen? Externe Orchestratoren bringen eine separate Control Plane mit, die unabhängig ausfallen kann – was gut ist, wenn Ihre Datenbank brennt.
4) Plattformgrenzen und Team‑Kapazität
Reality‑Check:
- Einschränkungen von Managed Postgres: Einige Provider beschränken C‑Extensions. RDS und Aurora Postgres erlauben nur einen Teil. Prüfen Sie, ob pg_durable dort läuft, wo Sie Postgres betreiben.
- Multi‑Region: Wenn Sie aktiv‑aktiv über Regionen brauchen, sind Orchestratoren mit eigenem Replikationsmodell oft einfacher, als Workflow‑Zustand über Regionen hinweg in Postgres zu schreiben.
- Ops‑Schlagkraft: Temporal oder Step Functions gut zu betreiben ist nicht kostenlos. Ebenso wenig, sich in Autovacuum, HOT‑Updates und partitionierte Job‑Tabellen einzuarbeiten.
Wo dauerhafte Ausführung in der Datenbank glänzt
1) Niedrige Latenz, zeilenlokale Workflows
Denken Sie an Anti‑Fraud‑Checks auf einer Checkout‑Zeile, Berechtigungsneuberechnung bei Planwechsel eines Users oder Aktualisierung materialisierter Views. Der Ausführungspfad lautet: „ein paar Zeilen anfassen, ein Event auslösen, einen Retry terminieren“. Jeder Schritt soll idempotent sein und mit derselben Transaktion wie die Zeilenänderung committen. In‑DB gewinnt bei Einfachheit, Konsistenz und Latenz (ein Netzwerk‑Hop weniger).
2) Strikte Kostentransparenz und kleinere Teams
Ein horizontal skalierbares System weniger zu betreiben senkt kognitive und finanzielle Kosten. Liegt Ihr Peak unter 5k Schritt‑Ausführungen/s und sind die Payloads klein, ist Postgres beim TCO schwer zu schlagen. Sie zahlen für größere IOPS‑ und Storage‑Reserven statt für einen Orchestrator‑Cluster oder pro Zustandsübergang in einem Cloud‑Service.
3) Compliance und Auditierbarkeit per SQL
Schritthistorie und Entscheidungen leben in Tabellen, die Sie joinen, per Snapshot erfassen und unter Ihrem bestehenden Compliance‑Regime exportieren können. Kein separater Audit‑Lake, der abgeglichen werden muss. SOC 2‑ und ISO 27001‑Prüfungen werden einfacher, wenn Sie Kontrolle mit SQL‑Queries nachweisen können.
Wo externe Orchestratoren weiterhin gewinnen
1) Wilder Fan‑out, lange Tails, menschliche Schritte
Hunderte paralleler Verzweigungen, Schritte, die stundenlang auf Callbacks warten, und menschliche Freigaben treiben Sie in die Orchestrierungswelt. Temporals Timer‑Wheels und Event Sourcing sind dafür gebaut. Diese Form in Postgres zu drücken, ohne den Rest Ihrer Workloads zu verhungern, ist eine Kunst, die Sie nicht lernen sollten.
2) Teamgrenzen und Service‑Isolation
Wenn Sie ein Scale‑up mit mehreren Teams sind, wird ein externer Orchestrator zur Plattform mit klaren APIs, Quoten und Mandantenfairness. Alle Workflow‑Zustände in ein DB‑Schema zu kippen, kann zu Noisy‑Neighbor‑Incidents und politischen Kämpfen um VACUUM‑Settings führen.
3) Multi‑Region‑Aktivität und Cloud‑Berechtigungsspagat
Globale SLAs und regionsübergreifende Workflows lassen sich leichter begründen, wenn der Orchestrator die Replikation abstrahiert. Und wenn Ihre Schritte Cloud‑Credentials für ein Dutzend Services brauchen, ist es oft besser, Secrets und IAM auf den Orchestrator zu beschränken, statt noch mehr Verantwortung an die DB‑Boundary zu schieben.
Die versteckten Kosten von in‑DB‑Workflows
Schreibverstärkung und VACUUM‑Budget
Jeder Schritt‑Übergang ist mindestens ein Write, oft zwei (Claim + Complete) plus ein Timer‑Write. Bei 2k Schritten/s und durchschnittlich 300‑Byte‑Zeilen erzeugen Sie Größenordnungen von 600 KB/s Tabellen‑Churn vor WAL‑Overhead. Der WAL‑Multiplikator kann je nach Indizes leicht das 2‑ bis 4‑Fache betragen. Das sind 1,2–2,4 MB/s WAL – also 100–200 GB/Tag. Planen Sie IOPS und Storage entsprechend. Planen Sie danach VACUUM: Wächst Ihr Autovacuum‑Rückstand, blähen Dead Tuples auf, HOT‑Updates werden schlechter und Replikas beginnen zu laggen.
Heiße Partitionen und Index‑Design
Timer erzeugen einen Hotspot rund um „next_due_at“. Sie brauchen partielle Indizes, Bucketing (z. B. nach Minute) und Partitionierung für große Deployments. Das ist Engineering, das Sie sonst Ihrem Orchestrator‑Anbieter mit der Kreditkarte bezahlt hätten.
Logische Replikation und CDC‑Nebeneffekte
Hochdrehende Workflow‑Tabellen können Ihren logischen Replikationsstrom dominieren und Downstream‑Konsumenten ersäufen. Nutzen Sie Publication‑Filter, um Workflow‑Schemata aus CDC auszuschließen, sofern nicht erforderlich. Wenn Sie sie nicht ausschließen können, erwägen Sie einen separaten Postgres‑Cluster für den Orchestrierungszustand.
Observability, die Sie bauen müssen
SQL macht ad hoc‑Debugging erfreulich. Aber Sie brauchen trotzdem Service‑Level‑Metriken: Schritt‑Start/Stopp‑Zählungen, Retries, DLQ‑Größe, Timer‑Wheel‑Lag, Executor‑Sättigung. Wenn die Extension das nicht exportiert, werden Sie es bauen. Diese Arbeit bleibt – Nearshore oder Inhouse.
Ein konkreter Entscheidungsrahmen
Bewerten Sie jede Aussage mit 0–2. Summieren Sie die Punktzahl.
- Über 80 % der Workflow‑Schritte lesen/schreiben Zeilen im selben Postgres‑Cluster wie Ihr Kernprodukt.
- p95‑Schrittlaufzeit unter 500 ms; 99th unter 5 s; Payloads unter 10 KB.
- Spitzen‑Dauerlast an Übergängen unter 5k/s; Fan‑out typischerweise unter 32 Verzweigungen.
- Single‑Region oder Active‑Passive ist akzeptabel; RTO in Minuten, nicht Sekunden.
- Ihr Postgres‑Provider erlaubt die benötigten Extensions; Ihr Team kann Autovacuum und Partitionierung tunen.
- Compliance profitiert von SQL‑nativer Auditierbarkeit der Workflow‑Historie.
- 10–12: In‑DB‑dauerhafte Ausführung ist wahrscheinlich der richtige Default. Bleiben Sie diszipliniert.
- 6–9: Gemischt. Starten Sie in‑DB für zeilenlokale Flows; lagern Sie Long‑Tail‑ oder Fan‑out‑lastige Flows an einen externen Orchestrator aus.
- 0–5: Nutzen Sie einen externen Orchestrator. Ihre Workload‑ oder Organisationsform wird die Datenbank bestrafen.
Wenn Sie sich für in‑DB entscheiden: Leitplanken gegen 3‑Uhr‑morgens‑Drama
1) Separates Schema, eventuell separater Cluster
Bewahren Sie Workflow‑Tabellen in einem eigenen Schema auf, mit Publication‑Filtern standardmäßig aus. Wenn Read‑Replikas Ihres Produkts oder CDC‑Konsumenten wegen Workflow‑Churn zu laggen beginnen, verlagern Sie den Workflow‑Zustand in einen zweiten Postgres‑Cluster, bevor Sie die Technologie wechseln. Zwei Postgres‑Cluster sind oft immer noch einfacher, als einen völlig neuen Orchestrator zu lernen.
2) Nach Zeit und Mandant partitionieren
Partitionieren Sie Job‑ und Historientabellen nach due_at‑Bucket und optional nach Mandant. Löschen Sie alte Partitionen durch Drop statt DELETE. Halten Sie das heiße Working Set so klein, dass VACUUM deutlich unter Ihren Retry‑Backoff‑Intervallen fertig wird.
3) Für Idempotenz und Effect‑Logs designen
Verlangen Sie für jeden externen Side Effect eine request_id. Speichern Sie eine Effect‑Log‑Tabelle, indiziert nach (request_id, target). Lassen Sie alle Worker dies prüfen, bevor sie einen Side Effect erzeugen. Das ist orchestrator‑agnostische Hygiene, die At‑least‑once‑Zustellung in Exactly‑once‑Effekte verwandelt.
4) Timer unter Budget stellen
Erlauben Sie nicht, dass jedes Team beliebige Wakeups plant. Bieten Sie feste Backoff‑Policies und begrenzte Retry‑Anzahlen pro Workflow‑Typ. Stellen Sie eine „Timer‑Schuld“‑Metrik bereit: Summe überfälliger Timer. Alarmieren Sie bei Wachstum. Diese Metrik verrät Ihnen Executor‑Hungersnöte schneller als Nutzerberichte.
5) Backpressure und Fairness
Verwenden Sie SKIP LOCKED oder Advisory Locks mit per‑Queue‑Limits. Stellen Sie sicher, dass High‑Value‑Queues nicht von massenhaften Low‑Priority‑Jobs verhungert werden. Setzen Sie pro Mandant Concurrency‑Limits. Erzwingen Sie sie in SQL, nicht nur im Worker‑Code.
6) WAL‑, IOPS‑ und VACUUM‑SLOs
Setzen Sie messbare SLOs: z. B. p95‑VACUUM‑Verzögerung unter 5 Minuten für heiße Partitionen; WAL‑Erzeugung unter 3 MB/s dauerhaft im Peak; Apply‑Lag der Replika unter 5 Sekunden für Nicht‑Workflow‑Schemata. Können Sie das nicht messen, fliegen Sie blind.
7) Failure‑Testing
Töten Sie einen Worker während eines Schritts. Starten Sie den Primary während eines Retry‑Sturms neu. Pausieren Sie Autovacuum. Messen Sie die Recovery‑Zeit und das Volumen doppelter Side‑Effect‑Versuche. Wenn diese Drills Schrecken einjagen, sind Ihre Defaults noch nicht sicher.
Wenn Sie bei einem externen Orchestrator bleiben: die Gewinne bewahren
- Schreiben nahe der Daten co‑lokalisieren: Auch mit Temporal oder Step Functions das Outbox‑Pattern nah an Ihren Domain‑Tabellen halten. Machen Sie den Schrittabschluss abhängig vom Outbox‑Write.
- Orchestrator nur für Long‑Tail nutzen: Ziehen Sie ein Hybrid in Betracht: in‑DB für kurze, zeilenlokale Workflows; extern für menschliche Schritte und großen Fan‑out. Ziehen Sie die Grenze nach Latenz und Aufbewahrung, nicht nach Team.
- Bill‑Shock kappen: Wenn Sie ein Per‑Transition‑Preismodell haben, ziehen Sie geschwätzige Retries in ein In‑Process‑Backoff, bevor sie den Orchestrator erreichen.
Was ist mit Agents und KI‑Workflows?
Agentische Systeme sind die denkbar schlechtesten Workflow‑Kunden: lange Ketten, spekulative Verzweigungen, Retries auf wackelige Tools und Megabyte‑Prompts in den Schritt‑Payloads. Widerstehen Sie der Versuchung, diesen Zustand in Ihre primäre Postgres zu legen. Wenn Sie in Postgres eine dünne Kontrollspur halten müssen (z. B. für Billing oder Audit), speichern Sie nur Referenzen und minimale Metadaten. Die Token‑Streams, Tool‑Logs und Scratchpads gehören in Blob‑Storage, indiziert von einem dedizierten Orchestrator oder einem Stream‑Prozessor, nicht in Ihre OLTP‑Datenbank.
Kostenmodellierung: einfache Mathematik, große Implikationen
- In‑DB: Peak 2k Transitions/s, 1 KB pro Transition Zustand/Log, WAL‑Faktor ~3x → ~500 GB/Monat an Storage‑Churn, plus Replikas. Angenommen eine kräftige Postgres‑Instanz für $2–4k/Monat und 2–3 Replikas. Engineering: 0,2–0,4 FTE eines Seniors mit Postgres‑Interna.
- Externer Orchestrator (self‑hosted Temporal): 3–5‑Node‑Cluster, $1–2k/Monat Infra, 0,3–0,6 FTE Platform‑Engineer. Bessere Isolation, mehr Software zu betreiben.
- Externer Orchestrator (managed/PaaS): Oft Preismodelle pro Transition. Bei $0,25 pro Million Zustandsübergänge (illustrativ; prüfen Sie Ihren Anbieter) sind 5B Transitions/Jahr $1,25M. Viele Teams unterschätzen diese Position, bis die Rechnung kommt.
Keiner dieser Werte ist für Sie „richtig“. Aber sie rahmen die Entscheidung: Zahlen Sie in Compute, Expertise oder Anbieter‑Marge?
Migrationen ohne Downtime
Von DIY‑Queues zu in‑DB‑dauerhafter Ausführung
- Führen Sie die Extension und neue Tabellen in einem separaten Schema ein.
- Schreiben Sie für einen Canary‑Subset Step‑Claims dual in alte und neue Job‑Tabellen.
- Schalten Sie Worker‑Konsumenten pro Workflow‑Typ hinter Flags um.
- Härten Sie VACUUM, Timer‑Lag und Idempotenz‑Checks; dann den Standard umlegen.
- Backfillen oder archivieren Sie die alte Job‑Historie in günstigen Storage und droppen Sie die heißen Partitionen.
Von in‑DB zu einem externen Orchestrator (der Notausgang)
- Fügen Sie eine Adapter‑Schicht hinzu, die Ihre Workflow‑Zustandszeile in einen Orchestrator‑Workflow‑Input übersetzt.
- Senden Sie neues Work extern, behalten Sie aber einen Postgres‑Pointer für Audit.
- Exportieren Sie für laufendes Work Timer als Orchestrator‑Timer an sicheren Übergabepunkten (nach Abschluss eines Schritts).
- Lassen Sie in‑DB‑Timer 1–2 Release‑Zyklen deaktiviert, aber wiederherstellbar, falls Sie zurückrollen müssen.
Ein Hinweis zu Nearshore‑Ausführung
Wenn Sie als US‑CTO mit einem brasilianischen Nearshore‑Team arbeiten, kann in‑DB‑dauerhafte Ausführung ein Velocity‑Multiplikator sein, wenn das Team Postgres ohnehin betreibt. Das Überschneidungsfenster (6–8 Stunden) reicht aus, um Schema und Ausführungssemantik schnell zu iterieren, und Sie vermeiden das Warten auf einen neuen Plattformvertrag oder eine SSO‑Integration. Der Trade‑off: Sie müssen operative SLOs und Runbooks früh niederschreiben – sonst sorgt die „Ist doch nur SQL“‑Haltung für unsichtbare Plackerei, die zum schlimmsten Zeitpunkt hochkommt.
Fazit
pg_durable bringt dauerhafte Ausführung in der Datenbank in Reichweite für Teams, die Temporal nie aufgesetzt hätten. Das ist gut fürs Shipping. Es ist gefährlich für Teams, die weniger Container mit weniger Komplexität verwechseln. Entscheiden Sie anhand Ihrer Workload‑Form und Ihres Fehlermodells – nicht nach Mode.
Wesentliche Erkenntnisse
- In‑DB‑Workflows glänzen bei latenzkritischen, zeilenlokalen Schritten mit enger transaktionaler Kopplung. Sie reduzieren Outbox/Relay‑Glue und ein bewegliches Teil weniger.
- Externe Orchestratoren gewinnen bei wildem Fan‑out, langen Wartezeiten, Multi‑Region‑Aktivität und Multi‑Team‑Isolation.
- Kosten modellieren: Postgres zahlt in WAL/IOPS und DBA‑Zeit; Orchestratoren zahlen in Clustern oder pro Transition in Dollar.
- Wenn Sie in‑DB gehen, budgetieren Sie für VACUUM, Partitionierung, Timer‑Fairness und WAL‑SLOs. Setzen Sie Timer und Retries unter Budget.
- Halten Sie einen sauberen Notausgang: separates Schema, Dual‑Write‑Canaries und klare Übergabepunkte zurück zu einem externen System, falls Sie Postgres entwachsen.