Making Postgres queues scale
Posted by KraftyOne 4 days ago
Comments
Comment by atombender 3 days ago
This is a serious problem because it affects the planner's ability to make good choices. Dead tuples are still indexed and the need to skip them isn't accounted for by the query planner, so a table with lots of dead tuples may perform really badly. The autovacuum process will be constantly chasing dead tuples, and you'll want to set the autovacuum settings to be very aggressive to be able to keep up.
My team has been starting to use PgQue [1] for a new application, and it seems really well-designed. PgQue is explicitly designed to solve the bloat problem, by avoiding tuple deletion. Instead of deleting processed tuples, it will periodically TRUNCATE the entire table. It uses two tables that it "flips" between so TRUNCATE can run on the inactive table while the active on is used for queuing. PgQue also uses a snapshot approach to avoid row-level locks.
PgQue also stands out in that its queue model is position-based, so it can implement nice features like collaborative consumers, fan-out, atomic batches, and "recover from last good" behaviour. It makes some compromises (no priority support, slightly higher latency), but they're fine for most use cases.
Previously discussed on HN here [2].
Comment by KraftyOne 3 days ago
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.
Comment by danielheath 3 days ago
Job workers could query the parent table, no need to modify them.
Comment by sgt 3 days ago
Comment by atombender 3 days ago
Comment by sgt 3 days ago
Comment by atombender 3 days ago
We were able to show that the dead tuples caused Postgres to use the wrong query plan because it misjudged the amount of real rows. We reported this problem on the Postgres mailing list, and it seemed like this was a known problem and that there was interest in making the planner more dead-tuple-aware.
Comment by dewey 4 days ago
That might have been the case 10 years ago. In the past years there have been many Postgres powered queueing systems and even Rails switched to Postgres powered queues by default (SolidQueue) more than 3 years ago.
Comment by mjfisher 3 days ago
But usually when I reach for a queuing system, it's because I want to decouple a part of the architecture. And in that case, it's probably better to use a dedicated queueing system instead of postgres.
I wonder if the two cases are conflated in a lot of online discussion.
Comment by dewey 3 days ago
I know it's very hard to compare workloads, but famous recent example: https://openai.com/index/scaling-postgresql/
> It may sound surprising that a single-primary architecture can meet the demands of OpenAI’s scale; however, making this work in practice isn’t simple.
Comment by tomnipotent 3 days ago
Comment by dewey 3 days ago
Comment by tomnipotent 3 days ago
This ignores the fine print. It scales a very long way under specific circumstances with specific workloads. The more write-heavy your workload the less eloquently Postgres scales.
For OpenAI's use case you could swap Postgres with MySQL and it would scale just as well.
Comment by dewey 3 days ago
Nobody is arguing for using it for everything and forever but for most company sizes it’s perfectly fine to not reach for a dedicated queuing tool if you already have PG running.
Comment by tomnipotent 3 days ago
Postgres doesn't do "tiny writes" - it writes whole pages multiple times on every update even if you're only changing 4 bytes, combined with even more writes later on when vacuuming tables. This is one of the reasons why the historical advice was to not build high-volume queues on top of Postgres and why "oh look another post about queues on Postgres" keeps soliciting comments like this.
Comment by ballon_monkey 3 days ago
Comment by tomnipotent 3 days ago
Comment by bel8 3 days ago
So I don't think they changed to PostgreSQL per se.
Comment by dewey 3 days ago
Comment by sorentwo 3 days ago
Shameless link to an older article about throughput with Oban (https://oban.pro/articles/one-million-jobs-a-minute-with-oba...), and in follow-up research we've sustained 12k/s with a p99 under ~100ms.
Comment by sebmellen 3 days ago
Comment by NightMKoder 3 days ago
The super advanced version of this is pgque - https://pgque.dev/ - but that’s more like Kafka in Postgres. I wouldn’t go there if you don’t know the Kafka model already and you want it.
Comment by richwater 3 days ago
Comment by 0x457 2 days ago
Comment by keeganpoppen 3 days ago
Comment by hiyer 3 days ago
Comment by LtdJorge 3 days ago
Comment by rtpg 3 days ago
I ended up finding a good number of HN comments like "we were doing this and regretting it".
So here's my ask: anybody here use PG for queues at a system with reasonable throughput, without regretting it? Like where there might be some contention
Comment by shakow 3 days ago
Comment by hmaxdml 3 days ago
For fair queuing you can have partitioned queues where only active partitions consume resources
Comment by rcleveng 3 days ago
Comment by everfrustrated 3 days ago