HN user

KraftyOne

1,180 karma

Hello! I'm Peter Kraft. I love databases and distributed systems and recently co-founded DBOS to help developers build reliable software effortlessly.

Website: http://petereliaskraft.net/

Posts75
Comments108
View on HN
www.dbos.dev 6d ago

Just Use Postgres for Job Queues

KraftyOne
7pts0
www.dbos.dev 20d ago

Postgres transactions are a distributed systems superpower

KraftyOne
232pts96
www.dbos.dev 26d ago

Just Use Postgres for Task Queues

KraftyOne
2pts0
brooker.co.za 27d ago

Is this blog written by AI?

KraftyOne
5pts0
github.com 28d ago

Show HN: DBOSify – Drop-in Temporal replacement built on Postgres

KraftyOne
91pts19
brooker.co.za 1mo ago

Surprising economics of load-balanced systems

KraftyOne
160pts37
www.dbos.dev 1mo ago

Just Co-Locate Data in Postgres

KraftyOne
2pts0
www.dbos.dev 1mo ago

Can Postgres Replace Task Queues at Scale?

KraftyOne
2pts0
www.apurvamehta.com 1mo ago

Our First Customers Were the Exception

KraftyOne
3pts0
www.dbos.dev 1mo ago

Can Postgres Queues Scale?

KraftyOne
4pts1
www.dbos.dev 1mo ago

Building durable workflows on Postgres

KraftyOne
359pts147
www.dbos.dev 2mo ago

Postgres Is All You Need for Durable Execution

KraftyOne
1pts0
brooker.co.za 2mo ago

What's Easy Now? What's Hard Now?

KraftyOne
4pts0
www.dbos.dev 2mo ago

Goto Considered Harmful: Why Event-Driven Is a Poor Architecture

KraftyOne
2pts2
www.dbos.dev 2mo ago

Build Reliable Notifications with Postgres

KraftyOne
2pts0
theconsensus.dev 2mo ago

Who even uses jemalloc anyway?

KraftyOne
3pts0
www.dbos.dev 2mo ago

Does Postgres Scale?

KraftyOne
192pts95
muratbuffalo.blogspot.com 2mo ago

Measuring AI Ability to Complete Long Software Tasks

KraftyOne
2pts0
www.dbos.dev 2mo ago

Benchmarking How Postgres Scales

KraftyOne
2pts0
www.dbos.dev 3mo ago

Async Python Is Secretly Deterministic

KraftyOne
79pts36
www.dbos.dev 4mo ago

Bridging Languages with Postgres and Workflows

KraftyOne
2pts0
www.dbos.dev 5mo ago

Making Workflows Work Right in Golang

KraftyOne
2pts0
www.dbos.dev 6mo ago

Building Decentralized Workflows That Scale

KraftyOne
2pts0
brooker.co.za 7mo ago

What Does a Database for SSDs Look Like?

KraftyOne
2pts0
www.dbos.dev 7mo ago

AI Agents Do Weird Things (and what to do about it)

KraftyOne
1pts0
github.com 8mo ago

Show HN: DBOS Java – Postgres-Backed Durable Workflows

KraftyOne
114pts57
muratbuffalo.blogspot.com 8mo ago

Disaggregated Database Management Systems

KraftyOne
2pts0
multigres.com 9mo ago

Generalized Consensus: An Alternate Approach to Distributed Durability

KraftyOne
3pts0
www.dbos.dev 9mo ago

Building Decentralized Workflows That Scale

KraftyOne
1pts0
brooker.co.za 9mo ago

Is Systems Research Just About Making Numbers Bigger?

KraftyOne
3pts1

Outbox's power is that it turns an atomicity problem into an idempotency problem. You atomically write to the outbox, then you have an idempotent "workflow" that processes events from the outbox. This turns "at most once" semantics (where an event could be dropped entirely) to "at least once" semantics (where the event processing could run multiple times). For many systems, that's a big improvement.

You still need idempotency for side effects outside your database, that's true (and fundamental to durability). But now you get exactly-once semantics for operations on your database, which can be quite valuable if your workflow performs many such updates or they're particularly complicated or stateful.

You build a distributed system on top of this! For example, you may have many distributed workers durably executing workflows from the Postgres-backed task queue. The Postgres transactions allow you to atomically perform operations spanning both your task queue and your business data.

Here's another blog post about how a Postgres-backed task queue can run at scale: https://www.dbos.dev/blog/making-postgres-queues-scale

The key is that the UDF's enqueue is transactional with the database update. Let's say the database update is inserting a new order. This provides the guarantee that if a new order is inserted, a job to process the order is also enqueued. It's impossible for a new order to be inserted without its processing job also being enqueued. Then the durable workflow/queue system is responsible for making sure the processing job, once enqueued, actually executes.

Based on the shown graph, this is misleading at best, essentially false. After 120K writes/s p50 spikes from 10ms to 1s (1 second for a write!!!!). That's two orders of magnitude latency spike, and an unacceptable one for an OLTP workload. It clearly shows the server is completely saturated, which is clearly a non operational regime. Quoting 144K is equivalent to quoting the throughput of a highway at the moment traffic comes to a standstill.

Based on this graph the highest number I'd quote is 120K. And probably you want to keep operating the server within a safe margin below peak, but since this is a benchmark, let's call 120K the peak. Because actually p50 is not even the clear-cut. It should be a higher percentile (say p95) at which latency is within reasonable bounds. But for the shake of not over complicating, it could be taken as a reference.

You definitely don't want to run a production system at saturation! But it's worthwhile to measure a complex system like Postgres at saturation, see when it gets there and how it behaves there, and then run at a slightly lower throughput.

Therefore, you are not measuring Postgres peak performance, but rather Postgres performance under the IO constraints of this particular system. Certainly, 120K IOPS is the maximum that this particular instance can have. But it doesn't show if Postgres could do better under a more performant IO disk. Actually, a good test would have been to try the next instance (db.m7i.48xlarge) with 240K IOPS and see if performance doubles (within the same envelop of p50 latency) or not. And afterwards to test on an instance with local NVMe (you won't find this in RDS).

I've done some testing (not in the blog post)--doubling instance size/IOPS doesn't improve performance significantly because it doesn't affect the WAL bottleneck. Local NVMe should have a significant impact in theory, but I haven't tested this myself.

300 seconds test duration?? This is not operational. You are not accounting for checkpoints, background writer, and especially autovacuum. Given that workflow pattern includes UPDATEs, you must validate bloat generation (or, equivalently, bloat removal) by a) observing much longer periods of time (e.g. 1h) and b) making sure the autovacuum configuration (and/or individual table vacuum configuration if required) makes bloat contained in a stable way. Otherwise, shown performance numbers will degrade over time, making them not realistic.

Those are usage examples (notice the 1000 rps)--actual benchmarks were run at and were stable at much longer duration.

Yes, this benchmark deliberately uses RDS defaults to make the comparison fairer/more general.

One warning--the setting that would increase throughput the most (synchronous_commit = off) sacrifices durability to do so.

It's been a stable (and documented) behavior of the Python standard library for almost a decade now. It's possible it may change--nothing is ever set in stone--but that would be a large change in Python that would come with plenty of warning and time for adjustment.

That's the cool thing about this behavior--it doesn't matter how complex your program is, your async functions start in the same order they're called (though after that, they may interleave and finish in any order).

Looking at the trial example, the way I would solve it is that when the user clicks "start trial", that starts a small synchronous workflow that first creates a database entry, then enqueues the onboarding workflow, then returns. DBOS workflows are fast enough to use interactively for critical tasks like this.

Yes, agree the correspondence is close, the primary difference is in form factor and not in underlying guarantees (but form factor matters! Building this as a library was technically tricky, but unlocks a lot of use cases). Reading the Orleans and early durable functions papers in grad school (and many of your papers) was definitely helpful in our journey.

versioning

Yes, something like that is needed, we're working on building a good interface for it.

transactional enqueueing

But it is safe as long as it's done inside a DBOS workflow. If the connection is lost (process crashes, etc.) after the transaction but before the child is enqueued, the workflow will recover from the last completed step (the transaction) and then proceed to enqueue the child. That's part of the power of workflows--they provide atomicity across transactions.

The Restate model depends on a long-running external orchestrator to do the "pushing". However, that comes with downsides--you have to operate that orchestrator and its data store in production (and it's a single point of failure) and you have to rearchitect your application around it.

DBOS implements a simpler library-based architecture where each of your processes independently "pulls" from a database queue. To make this work in a serverless setting, we recommend using a cron to periodically launch serverless workers that run for as long as there are workflows in the queue. If a worker times out, the next will automatically resume its workflows from their last completed step. This Github discussion has more details: https://github.com/dbos-inc/dbos-transact-ts/issues/1115

Thanks for the great questions!

1. Yes, currently versioning is either automatically derived from a bytecode hash or manually set. The intended upgrade mechanism is blue-green deployments where old workflows drain on old code versions while new workflows start on new code versions (you can also manually fork workflows to new code versions if they're compatible). Docs: https://docs.dbos.dev/java/tutorials/workflow-tutorial#workf...

That said, we're working on improvements here--we want it to be easier to upgrade your workflows without blue-green deployments to simplify operating really long-running (weeks-months) workflows.

2. Could I ask what the intended use case is? DBOS workflow creation is idempotent and workflows always recover from the last completed step, so a workflow that goes "database transaction" -> "enqueue child workflow" offers the same atomicity guarantees as a workflow that does "enqueue child workflow as part of database transaction", but the former is (as you say) much simpler to implement.