HNHacker News
TopNewBestAskShowJobs

KraftyOne

1,457 karma · joined June 20, 2019

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/

submissionscomments
KraftyOne··on Making Postgres queues scale
This is something we mention in the third section of the article: dead tuples and autovacuum caused real performance hits, which we (partially) mitigated through index optimization (minimizing the number and size of indexes, and making indexes partial).

PgQue is a really interesting system! However, its semantics are quite a bit more similar to Kafka than to a job queue, which is good for some workloads and not for others. For example a truncation-based deletion system is fast but inflexible, and not suitable for a job queue system because a single long-running job (and DBOS supports workflows that run for months) can block truncation.

KraftyOne··on Postgres LISTEN/NOTIFY actually scales
The core optimization is to buffer notifications in-memory and send them in a batch instead of sending them as part of every transaction. So that's a general-purpose optimization for Postgres apps using LISTEN/NOTIFY.
KraftyOne··on Postgres LISTEN/NOTIFY actually scales
If you mean the optimizations coming in Postgres 19, the original post addresses this:

> As an aside, there’s been some online discussion of a Postgres patch (https://github.com/postgres/postgres/commit/282b1cde9dedf456...) related to this issue. This patch (to be released in Postgres 19) does not remove the global lock or fix the bottleneck we observed. Instead, it optimizes the narrower case where there are many notification channels and each listener is waiting only on a specific channel.

KraftyOne··on Postgres LISTEN/NOTIFY actually scales
To be clear, it's not a custom patch to pg itself, but an application-side buffering and batching optimization.
KraftyOne··on Postgres transactions are a distributed systems superpower
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.
KraftyOne··on Postgres transactions are a distributed systems superpower
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.
KraftyOne··on Postgres transactions are a distributed systems superpower
That's what the post is about! Once you're doing that, you really do have transactions between the state and the queue.
KraftyOne··on Postgres transactions are a distributed systems superpower
Every item will be written to the queue exactly once (as the update is transactional). Queue processing may need at-least-once semantics, yes, depending on what exactly you're doing.
KraftyOne··on Postgres transactions are a distributed systems superpower
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

KraftyOne··on Postgres transactions are a distributed systems superpower
Yes, the core design is building a workflow system on a database--essentially, replacing the central orchestrator most workflow systems use with a Postgres database. This previous blog post goes into more detail: https://www.dbos.dev/blog/postgres-is-all-you-need-for-durab... (HN discussion: https://news.ycombinator.com/item?id=48313530)
KraftyOne··on Postgres transactions are a distributed systems superpower
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.
KraftyOne··on Postgres transactions are a distributed systems superpower
Exactly! It's a distributed system, with many processes performing work in parallel, with a central database as a coordination point, used as little as possible. A mutex wouldn't get quite the same performance :)
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
Unfortunately, DSQL is still lacking many core Postgres features (most notably foreign keys) that DBOS and DBOSify depend on. DBOS works with most flavors of Postgres though, including regular Aurora/RDS, Cloud SQL, AlloyDB, Supabase, Neon, CockroachDB, and others.
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
Take any Temporal example and swap the imports and connection strings, it's a drop-in replacement :)
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
We'd need DBOS in C# first! It's something we're considering for the future--if you're working in C#, happy to chat about it, reach out at peter.kraft@dbos.dev.
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
Exactly, Postgres as an orchestrator instead of a Temporal server as an orchestrator. This page goes into more detail (for DBOS, but DBOSify is the same principle but using the Temporal API directly): https://docs.dbos.dev/explanations/comparing-temporal
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
I'm very sorry about this. I'll figure out why the form wasn't responded to. In the meantime, email me directly at peter.kraft@dbos.dev.
KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
Yes it can (with documented exceptions)! The suite is here: https://github.com/dbos-inc/dbosify-py/tree/main/tests/confo...

Here's the documentation: https://github.com/dbos-inc/dbosify-py/blob/main/docs/ARCHIT...

KraftyOne··on Show HN: DBOSify – Drop-in Temporal replacement built on Postgres
Hi HN, I'm Peter, creator of DBOSify. Here to answer any questions you have!
KraftyOne··on Building durable workflows on Postgres
Secrets are orthogonal to durable execution--what are your concerns about using them together?
KraftyOne··on Does Postgres Scale?
> 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.

KraftyOne··on Does Postgres Scale?
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.

KraftyOne··on Async Python Is Secretly Deterministic
Yes, this only applies for tasks created from the same (sync or async) function. If tasks are creating other tasks, anything is possible.
KraftyOne··on Async Python Is Secretly Deterministic
It's documented behavior for the low-level API (e.g. asyncio.call_soon https://docs.python.org/3/library/asyncio-eventloop.html#asy...). More broadly, this has been a stable behavior of the Python standard library for almost a decade now. If it does change, that would be a huge behavioral change that would come with plenty of warning and time for adjustment.
KraftyOne··on Async Python Is Secretly Deterministic
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.
KraftyOne··on Async Python Is Secretly Deterministic
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).
KraftyOne··on Show HN: DBOS Java – Postgres-Backed Durable Workflows
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.
KraftyOne··on Show HN: DBOS Java – Postgres-Backed Durable Workflows
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.
KraftyOne··on Show HN: DBOS Java – Postgres-Backed Durable Workflows
> 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.

KraftyOne··on Show HN: DBOS Java – Postgres-Backed Durable Workflows
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

Page 1 of 4Next →