Devious SQL: Message Queuing Using Native PostgreSQL
blog.crunchydata.com
blog.crunchydata.com
There is a Go port of Que but you can also easily port it to any language you like. I have a currently non-OSS implementation in Rust that I might OSS someday when I have time to clean it up.
Having queues on Postgres is going to be such a great addition to my tool belt especially since there's always a Postgres instance running somewhere anyway.
I know the first use case that jumps to mind is a job queues, but I feel like the method described in the article is quite low level which means it can be used as a base to solve many use cases.
* I won't have to reach for Kafka/Redpanda unless the rate of events/messages reaches 100k-1000k per day.
* I can add one column called queue_id where each unique queue_id refers to a new queue which means I can use a single table for multiple queues.
* If I add a new column called event_type, then I wonder if it's possible to create a composite event queue, where for example the query must return exactly two rows and one row must have event_type=type_1 and second row must have event_type=type_2 and both rows are locked and processed exactly once?
I've implemented job queues in PostgreSQL, but I do polling in two phases. One phase to update the rows (using SKIP LOCKED) with metadata around the fact that a job has started, when it started, what agent is running the job, etc. That enables monitoring for stuck jobs, looking at current status, incremental updates on long running jobs, cooperative job cancellation, etc. It requires monitoring for abandoned jobs, but that's not too bad - it also avoids long-running transactions.
The other phase is to copy completed / aborted jobs to a history table for stats, failure reasons etc., keeping the main queue table small.
For example the database based queue we use in production is even more primitive than what this article describes (no batches, polling once a second, rows never deleted), but works perfectly fine since we're only processing less than 10 jobs/s and don't expect it to grow beyond 100 jobs/s.
> Queueing jobs in Node.js using PostgreSQL like a boss
You can scale much higher in Postgres 14 if you use partitioned tables, both horizontally and in terms of day-to-day stability because old partitions can be dropped in O(1) time, taking all table bloat with them. Obviously more work to set up, though.
The basic gist of it is that on one end a producer pushes to the list and a consumer(s) on the other end pops the job and executes it. Fire and forget style.
Why do you consider it bad for local dev? A Redis instance literally takes 1MB memory when started.
https://pypi.org/project/redis/
or run within python,
https://github.com/yahoo/redislite
it's pretty much the easiest thing to deploy.
[0] - https://github.com/to11mtm/oddjob/tree/cleaning-aug-19
So, for small (but critical nonetheless) use cases, do any of you use your own custom built queues on top of a database?
Next time someone says it’s a bad idea, keep pressing for real answers, not emotion.
I couldn't find the original video describing their architecture. (It's private now, I guess).
My usage is much lower than that but I want to target that number.
Works great and reliably so far. I think our current volume is on the order of 100-1000 req/sec. We'll likely switch to graphile-worker when we need more performance, but we're all about avoiding premature optimization. That library has been benchmarked to handle 10k req/sec: https://github.com/graphile/worker#performance
This is mainly because PgSQL based queue is durable by default and based on how you implement your inserts, every new message will need a disk based commit.
Linux file move is atomic, which means file system based queues are perfectly viable. Just save one message per file. Move the file to a different directory when it changes state.
I built a prototype queue around this mechanism. Performance was bound to the disks ability to create small files, around 30,000 messages per second.
This sort of performance rivals some of much more sophisticated and complex queuing systems, but has zero configuration.
It's surprisingly difficult to write something that stores changing data on disk correctly and durably.
If you roll your own, that's very hard to get right. I wouldn't have confidence in such a system that I've built myself.