Peter Kraft, Mitentwickler bei DBOS, startet den Benchmark ein letztes Mal. Am Ende steht im Terminal eine Zahl, die auf den ersten Blick nach einem Kommafehler aussieht: eine Million. Eine Million dauerhafte Workflows, gestartet, ausgeführt und protokolliert in einer Sekunde – auf einer gewöhnlichen Postgres-Datenbank, nicht auf einem eigens gebauten Verteilsystem mit Dutzenden Spezialdiensten.
In einem Blogpost beschreibt er diesen Lauf und die Arbeit dahinter. Seine These ist unbequem für alle, die Workflow-Engines als eigene Infrastrukturklasse betrachten: Durable Execution braucht kein separates Ökosystem, wenn die Datenbank ohnehin alles mitbringt, was dafür nötig ist. Postgres als Workflow-Engine ist damit keine Notlösung, sondern eine Architekturentscheidung, die sich mit Zahlen belegen lässt.
Der Vorteil liegt in der Vereinfachung. Wer heute durable Workflows braucht, greift meist zu einem Message Broker, einer Orchestrierungsplattform und einer Datenbank – drei Systeme, die konsistent gehalten werden müssen. Der Ansatz aus dem Artikel dreht das um: ein Speicher, ein Transaktionsmodell, ein Backup.
Was ein durable Workflow ist – und warum das Fahrtenbuch entscheidet
Ein dauerhafter Workflow ist im Kern ein Ablauf, der einen Absturz überlebt. Jeder ausgeführte Schritt wird festgehalten, bevor es weitergeht. Bricht der Prozess mittendrin ab, kann ein neuer Prozess an der letzten gespeicherten Stelle weitermachen, ohne Arbeit zu doppeln oder halb ausgeführte Zustände zu hinterlassen.
Denk an ein Fahrtenbuch in einem Lieferwagen. Nicht die Fahrt ist das Kunststück, sondern dass nach einem Motorschaden rekonstruierbar bleibt, welche Stopps schon erledigt waren. Ohne dieses Buch fährt der nächste Fahrer die ganze Tour noch einmal. Bei durable Workflows übernimmt die Datenbank diese Rolle: Sie ist das Buch, in das jeder abgeschlossene Schritt eingetragen wird.
Bei DBOS ist dieses Buch Postgres selbst. Die Bibliothek schreibt Checkpoints in Tabellen einer Systemdatenbank, zusammen mit dem Zustand des Workflows. Es gibt keinen zweiten Speicher, keinen Event-Log-Dienst, keine separate Wahrheit. Deshalb ist die Frage nach der Performance hier so wichtig: Wenn jede Zeile des Buchs einen eigenen Schreibvorgang kostet, wird es teuer.
Teuer heißt in diesem Fall: langsam. Ein Commit mit garantierter Persistenz auf Platte kostet Zeit, und diese Zeit multipliziert sich mit der Zahl der Schritte, wenn man naiv vorgeht.
Warum die naive Umsetzung bei ein paar tausend Workflows hängt
Die einfache Variante sieht so aus: Workflow starten, Zeile einfügen, Schritt ausführen, Zeile aktualisieren, nächster Schritt. Jeder Vorgang ist eine eigene Transaktion, jede Transaktion mindestens ein Netzwerk-Roundtrip zur Datenbank, und jeder Commit wartet darauf, dass Postgres den Write-Ahead-Log auf die Platte gebracht hat.
In dieser Bauform ist nicht die Datenbank der Flaschenhals, sondern die Latenz zwischen Anwendung und Datenbank. Man bezahlt für jede Zeile den vollen Weg. Bei ein paar tausend Workflows pro Sekunde ist auf einer normalen Verbindung Schluss. Das hat wenig mit der Leistungsfähigkeit von Postgres zu tun, viel mit der Zahl der Hin-und-Her-Bewegungen.
Der Entwickler beschreibt genau das als das Problem. Nicht die Datenbank war zu langsam, sondern die Art, wie sie benutzt wurde. Ein vertrautes Muster: Wer tausend einzelne Abfragen schickt, misst die Netzwerklatenz und nennt das Ergebnis Datenbankperformance.
Viele Workflows pro Transaktion statt einer pro Schritt
Die Lösung im Artikel ist unspektakulär und wirksam. Statt jeden Checkpoint einzeln zu schreiben, sammelt die Engine Einträge und schreibt sie gebündelt. Viele Workflow-Schritte landen in einer Transaktion, viele Zeilen in einem Statement. Postgres kann das gut, weil es Arrays und Mengenoperationen beherrscht: Statt hundertmal eine Zeile einzufügen, kommt ein Aufruf mit hundert Werten.
Damit sinkt die Zahl der Roundtrips drastisch, und der teure Teil der Arbeit – das Persistieren des Logs – passiert einmal für ein ganzes Bündel. Das ist derselbe Mechanismus, den Datenbanken intern beim Group Commit ohnehin nutzen. Man verwendet also eine Eigenschaft, die schon da war, statt sie mit zusätzlicher Infrastruktur nachzubauen.
Der zweite Teil des Hebels ist die Zustandslosigkeit der Engine. Weil der gesamte Zustand in Postgres liegt, kann man beliebig viele Engine-Instanzen davorstellen. Skalierung heißt dann nicht, ein komplexes Clusterprotokoll zu beherrschen, sondern weitere Prozesse zu starten, die auf dieselbe Datenbank zeigen. Eine skalierbare Workflow-Engine wird damit zu einer Frage der Datenbankkapazität – und die lässt sich mit Read Replicas, Partitionierung und schnelleren Platten beeinflussen.
Zur Einordnung: Der Benchmark misst kurze, checkpoint-lastige Workflows. Dort wirkt Batching am stärksten. Bei Workflows mit viel echter Arbeit pro Schritt fällt der Schreibanteil ohnehin kleiner aus – dann ist die Zahl weniger spektakulär, aber auch weniger relevant.
Was eine Million Workflows pro Sekunde konkret bedeutet
Die Zahl ist kein Versprechen für deine Anwendung. Sie beschreibt eine Obergrenze unter Laborbedingungen: optimierte Pfade, wenige Roundtrips, ein klar definierter Ablauf pro Workflow. In der Praxis kommen Fachlogik, externe API-Aufrufe und variable Laufzeiten dazu, und die verschieben das Bild schnell.
Trotzdem zeigt die Zahl eine Größenordnung. Durable Workflows mit Postgres zu skalieren ist nicht auf den Bereich begrenzt, in dem Workflows seltene Ereignisse sind. Man kann sie so selbstverständlich einsetzen wie Datenbankzugriffe – als Standardbaustein für alles, was mehrere Schritte hat und nicht halb fertig liegen bleiben darf.
Das verändert die Architekturfrage. Statt zu überlegen, ob ein Ablauf „wichtig genug“ für einen durable Workflow ist, macht man die Persistenz zum Normalfall. Jeder Bestellvorgang, jede Synchronisation, jede mehrstufige Integration wird als Workflow modelliert, weil der Overhead klein genug ist. Workflow-Orchestrierung mit Postgres wird damit zur Default-Einstellung statt zur Ausnahme.
Der Autor bleibt nüchtern: Der Durchsatz hängt an der Datenbank, und wer mehr braucht, muss mehr Datenbank hinstellen. Das ist die ehrliche Antwort. Kein Trick hebt die physikalischen Grenzen eines Speichers auf – es gibt nur weniger Verschwendung auf dem Weg dorthin.
Wo du anfangen solltest, wenn du Postgres als Workflow-Engine nutzt
Der erste Schritt ist nicht Performance, sondern Korrektheit. Ein Workflow muss idempotent wiederaufsetzbar sein, und jeder Schritt braucht eine klare Grenze, an der der Checkpoint geschrieben wird. Solange das wackelt, hilft kein Batching der Welt. Erst wenn der Ablauf deterministisch fortsetzbar ist, lohnt der Blick auf den Durchsatz.
Der zweite Schritt ist die Tabellenstruktur. Eine Tabelle mit allen Workflows aller Zeiten wird irgendwann zum Hotspot, weil jede Statusänderung dieselben Indexseiten anfasst. Partitionierung nach Zeit oder Mandant, schmale Indizes und ein sauberes Archivierungskonzept sind hier mehr wert als jede Mikrooptimierung im Anwendungscode. Wer viele gleichzeitige Updates auf denselben Zeilen hat, misst schnell Lock-Konflikte statt Rechenleistung.
Der dritte Schritt ist das Bündeln. Wenn dein Anwendungsfall es zulässt, Schreibvorgänge zu sammeln, tu es bewusst. Ein Batch von hundert Checkpoints in einer Transaktion ist meist deutlich günstiger als hundert Transaktionen – nicht weil Postgres schneller rechnet, sondern weil du dir neunundneunzig Wartezeiten sparst. Genau dieser Effekt macht eine Million durable Workflows pro Sekunde auf Postgres überhaupt möglich.
Der vierte Schritt ist Messen statt Glauben. Der Blogpost ist ein Datenpunkt, keine Garantie für dein Setup. Bau einen Benchmark mit deinen echten Workflows, miss die Latenz pro Schritt und schau, wo die Zeit tatsächlich hingeht. Meistens liegt sie nicht dort, wo man sie vermutet.
Am Ende steht eine ernüchternde Erkenntnis: Dauerhaftigkeit ist kein Feature, das man zukaufen muss, sondern eine Eigenschaft, die eine ordentlich benutzte relationale Datenbank seit Jahrzehnten mitbringt. Der Beitrag von DBOS zeigt, wie weit man damit kommt, wenn man aufhört, jede Zeile einzeln zum Notar zu schicken.
Quelle: dbos.dev
