9 ms·
Postgres as queue
- adaboese 3y agoPostgres is extremely expensive to scale. Why on Earth would you try to put queue there.
- kromem 3y agoThe mantra about premature optimization applies to infrastructure too.
- valenterry 3y agoMaintaining extra infrastructure is expensive. Working around missing ACID is expensive. Depending on how many messages we are talking about, the cost of scaling postgres a bit more might be much lower.
- simonw 3y agoIf you're running less than a million tasks a day through a queue and you already have PostgreSQL in your stack why add anything else?
- adaboese 3y agoJust use a dedicated tool. It is not that hard. If you want higher level abstraction, you have a whole spectrum of next gen queues, like Temporal, Trigger.dev, Inngest, Defer, etc.
- jitl 3y agoPersonally the next-gen-ness of an infrastructure component is inversely proportional to my trust in it.
- simonw 3y agoWhy use a dedicated tool if you have something in your stack that can solve the problem already? The less separate pieces of infrastructure you can run, the less likely something will break that you don't know how to easily fix. The article touched on this in the list of things to avoid when it said "I estimate each line of terraform to be an order of magnitude more risk/maintenance/faff than each line of Python" and "The need for expertise in anything beyond Python + Postgres"
- CyberDildonics 3y agoEven a million tasks a day is less than 12 a second. Most queues are going to have surges since that's part of the point of a queue, but it's still a few orders of magnitude away from what should overwhelm a database.
- throwaway2990 3y agolol no it’s not.
- cwbriscoe 3y agoAny DB is fine as a queue depending on requirements, design and usage.
- CubsFan1060 3y agohttps://microservices.io/patterns/data/transactional-outbox.html https://microservices.io/patterns/data/transactional-outbox.... It allows you to wrap it all in a transaction. If you separate a database update and insertion into a queue, whichever happens second may fail, while the first succeeds.
- illusive4080 3y agoTell the alternative is a saga pattern to implement a distributed two phase commit. Or actual XA, but that is cursed.
- denistaran 3y agoSay you're running a SQL transaction and queue a message in SQS. The problem is, this message isn't part of your SQL transaction. So if your transaction fails, the SQS message won't roll back with it, potentially leading to inconsistencies. That's why sometimes it's better to use a queue inside an SQL database, to ensure both your transactions and queue messages are always in sync, and as a bonus, it simplifies the overall architecture and you will have fewer potential points of failure.
- beeboobaa 3y agobecause you're not expecting to have to scale beyond 1 instance for the next few years & are already using postgres & now everything is trivially transactional. KISS
- wavemode 3y agoIf the choice is between using a dedicated queue and postgres for your data vs. using postgres for both, using postgres for both makes perfect sense. The scale at which you would outgrow using postgres for the queue, you would also outgrow using postgres for the data.
- halfcat 3y agoAt what point does one outgrow Postgres for the data?
- cha42 3y agoIf you have an infra that need to scale so much then Postgresql isn't the right tool indeed. The right tools for your use case probably doesn't even exists and you will have to build one. It is not a mystery why all webscale companies endup designing their own DB technology. That being said, most of the DB in the wild are not remotely at those scale. I have seen my share of Postgresql/ElasticSearch combo to handle below TB data and just collapsing because of the overeng of administrating two DB in one app.
- alerighi 3y agoIf you need scaling. Not all applications need scaling (e.g. I'm doing an internal tool for a company that has 1000 employees, it's unlikely that from one day to another that number of employee will double!), and for most applications a single PSQL server either locally or in the cloud is enough.
- andrewstuart 3y agoPostgres isn’t the only way to have fun. Other things just as good at being a queue: SQLite SQL server MySQL Plain old files on the file system
- bhaney 3y agostd::queue and a big swap file
- cha42 3y agoyou can certainly encode queue in many ways. mkfifo just work. But integrating the queue in the DB isn't a bad idea if you want to have both the queue and the db in a shared state. I am happy that my queue inherit ACID properties. SQLite simply doesn't allow concurrent write so it is a no go for a queue. I don't know much about SQL Server and MySQL but I wouldn't favor a lockin closed source software or anything remotely connected to Oracle. At the end, only Postgresql remains I guess. Also, Postgresql is super solid and the closest to SQL standard.
- andrewstuart 3y agoYou’re wrong that I’m wrong.
- tarruda 3y ago> mkfifo just work Are you certain about that?
- otoburb 3y ago>>I don't know much about SQL Server and MySQL but I wouldn't favor a lockin closed source software or anything remotely connected to Oracle. If that's a concern, then MariaDB is another alternative. Postgres isn't the only option in town. EDIT: I'm correcting myself here because as far as I can tell, LISTEN NOTIFY equivalent capabilities are only available in MariaDB's SkyQL fully managed services offering, and not in the base MariaDB Community Server.
- tussa 3y agoDon't forget the plain old postit notes.
- dharmab 3y agoPostgres as a queue is one of the worst decisions I've seen made and I and others I've worked with have spent years of our lives unwinding the consequences.
- mikercampbell 3y agoIt’s almost as if we were in an ordered line, all waiting to learn the same thing, and had the lesson delivered no more than once.
- dralley 3y agoCompletely disagree for our use case. If your messages aren't send-and-forget but rather represent work that needs to be tracked, it is incredibly difficult to manage the state when your database and queue are separate. Using postgresql as the queue and leveraging the same transactions as your business logic solves many many issues.
- nostrebored 3y agoHow is it incredibly difficult? I’ve seen this work very well. Is your code handling different varieties of event states stored in your database? Why implement this as a monolithic data source? Was it not possible to separate different states by additional queues? Curious to hear the decision making process here. Maybe I can see it for low volume?
- jvans 3y agodralley is saying your queue push and database write aren't transactional. You have to be ok suffering some small % of message loss or messages sent without a database commit
- nostrebored 3y agoDepends on your queue semantics, ie is it just an at least once delivery or is there an ack back to the queue service. If there is a two phase process then you just need idempotence and can safely transact with the db.
- nomdep 3y agoWhy? Because memory is expensive and disk space is cheap
- notnmeyer 3y ago> I'd estimate each line of terraform to be an order of magnitude more risk/maintenance/faff than each line of Python. ehhhhhhhh, i don’t know about that. different strokes and all of that, but TF describing infrequently changing resources requires next to no maintenance. i’m not sure i under what they mean by “risk” in this context though. if you don’t know $technology well then any thing you do with it is likely to be worse/riskier than using the thing that you do know. > The need for expertise in anything beyond Python + Postgres. this should be the first point because it explains 99% of the motivation for all of this.
- smashed 3y agoYes, I think the point being made is that the infrastructure described in terraform is a much more difficult thing to change and evolve over time. Changing a source file line and shipping a new version of the app is much easier. They are not really comparable things at all. Even though we moved to infrastructure as code, that does not mean that the infra code is like functional application code. Come to think of it, I guess it was a damn good lie to sell infrastructure as code as so easy it's like shipping a new app version when it's everything but..
- benlivengood 3y agoTerraform providers and modules and the cloud resources evolve fairly rapidly (especially anything kubernetes-related). If you're not keeping up with the latest versions you can end up somewhat stranded when a new resource feature needs to be used and you discover unexpected tech debt from the world moving out from under you.
- Too 3y agoIt’s not that the TF code in itself is more complex, it’s the resources you spin up using TF that are. They are stateful, expose network interfaces, require backups, updates, cert renewals and all other kinds of operational burden.
- jnsaff2 3y agoIn Elixir land Oban[0] uses Postgres as queue and seems to work quite well. [0] - https://github.com/sorentwo/oban https://github.com/sorentwo/oban
- thibaut_barrere 3y agoConfirmed, it works fairly nicely (although we've had to be a bit careful with very chatty jobs configurations with regard to DB overload).
- jpb0104 3y agoIn the world of Ruby, GoodJob [0] has been doing a _good job_ so far. [0] - https://github.com/bensheldon/good_job https://github.com/bensheldon/good_job
- glenngillen 3y agoAnd in Go you’ve got River: https://riverqueue.com/ https://riverqueue.com/
- parthdesai 3y agoFWIW, River was directly inspired by Oban: https://news.ycombinator.com/item?id=38351067 https://news.ycombinator.com/item?id=38351067
- nemo44x 3y agoI’m still waiting for the “Postgres as a Web Browser” thread.
- nurettin 3y agoPostgres rust rewrite will be a huge thread.
- cdelsolar 3y agoPlease no
- jesperwe 3y agoIt has already started. https://docs.rs/pgx/latest/pgx/ https://docs.rs/pgx/latest/pgx/ Not hard to see path ahead of us similar to Linux's gradual step towards Rust in the kernel.
- tpetry 3y agoPostgres Rust extensions is a completely different topic than rewriting PG in Rust. The problem is that a crash in any extension will take down the full PG worker. So you can havoc the robustness of your PG database with any extension. But with a memory-safe language like Rust which forces you to handle all error cases this can‘t happen anymore.
- RedShift1 3y agoOur scientists are not stopping to think if they should.
- falsandtru 3y agoFDB, a distributed DB, would be superior if complete order is not required. https://apple.github.io/foundationdb/queues.html https://apple.github.io/foundationdb/queues.html
- tomas789 3y agoIt was used in my previous job and it worked wonders. It was an internal tool which stored lots of data but did not see high traffic. And as such the load was not an issue. In the world of thet large corporate it was almost the only viable solution. DB already existed so it could be used. Other solutions would require an “architecture” documents, security meetings and perhaps even hiring someone to care about new tech in stack.
- diek 3y agoPostgres is great as a queue, but this post doesn't really get into the features that differentiate it from just polling, say SQL Server for tasks. For me, the best features are: * use LISTEN to be notified of rows that have changed that the backend needs to take action on (so you're not actively polling for new work) * use NOTIFY from a trigger so all you need to do is INSERT/UPDATE a table to send an event to listeners * you can select using SKIP LOCKED (as the article points out) * you can use partial indexes to efficiently select rows in a particular state So when a backend worker wakes up, it can: * LISTEN for changes to the active working set it cares about * "select all things in status 'X'" (using a partial index predicate, so it's not churning through low cardinality 'active' statuses) * atomically update the status to 'processing' (using SKIP LOCKED to avoid contention/lock escalation) * do the work * update to a new status (which another worker may trigger on) So you end up with a pretty decent state machine where each worker is responsible for transitioning units of work from status X to status Y, and it's getting that from the source of truth. You also usually want to have some sort of a per-task 'lease_expire' column so if a worker fails/goes away, other workers will pick up their task when they periodically scan for work. This works for millions of units of work an hour with a moderately spec'd database server, and if the alternative is setting up SQS/SNS/ActiveMQ/etc and then _still_ having to track status in the database/manage a dead-letter-queue, etc -- it's not a hard choice at all.
- eddd-ddde 3y agoThere's actually an extension called "tcn" or trigger change notification that provides such a trigger put of the box.
- foofie 3y agoExcellent comment. Thank you for taking the time to write it.
- leontrolski 3y agoThanks for the comprehensive reply, does the following argument stand up at all? (Going on the assumption that LISTEN is one more concept and one less concept is a good thing). If I have say 50 workers polling the db, either it’s quiet and there's no tasks to do - in which case I don't particularly care about the polling load. Or, it's busy and when they query for work, there's always a task ready to process - in this case the LISTEN is constantly pinging, which is equivalent to constantly polling and finding work. Regardless, is there a resource (blog or otherwise) you'd reccomend for integrating LISTEN with the backend?
- osigurdson 3y agoIt is all about numbers. What latency is acceptable in this hypothetical? How many workers are there? I'm not sure how long x is "time.sleep(x)" but unless very long this will use one connection per worker. That is probably fine in this situation but it isn't fine for all projects.
- marcusbuffett 3y agoI looked at some solutions that were more specifically meant for queues, for my site, but decided to keep the infra simple by using my Postgres DB for the worker queues. There’s 10,000 jobs a day, at that rate theres virtually zero additional load. It runs fast, I know exactly how it works, can extend it easily, local dev doesn’t get any more complicated, and there’s not another point of failure in the form of a new piece of infra. If I get millions of users I’ll swap it out, in the meantime it took like a day to implement and I haven’t looked back.
- brycelarkin 3y agoBig fan of Graphile Worker to handle this job. https://github.com/graphile/worker https://github.com/graphile/worker
- jawngee 3y ago+1 graphile worker. we use it to coordinate all of our background tasks and events (use slayQ https://slayq-docs.vercel.app https://slayq-docs.vercel.app which is kind of a wrapper around it)
- andrew-v 3y agoSKIP LOCKED is a good idea to make sure that the tasks can be processed independently by the workers, but what about a case where you have to guarantee the order of events? I guess this Postgres queue is a very handy tool for cases like background jobs, but will it work with event sourcing?
- ffsm8 3y agoYou're responsible for writing the query that selects the tasks to process, so yes? You can obviously group by a common id, order by inserted and use limit 1. And if every event has to be processed in order, then you've created a system that can't be parallel, so you'll be effectively limited to a single worker.
- dkersten 3y agoYou can have multiple workers, but they all need to process all events. So it’s not suitable for parallelisation of work (how can it be, if total ordering is required), but it can be used where different workers consume the queue in different ways (eg for synchronising different services).
- Rapzid 3y agoOrder by insert time on some group key(or otherwise figure out the order) and select the top item. Then select that item for update with skip locked. If it's locked you will get zero results.
- rubenfiszel 3y agoIf you need a job queue on Postgres, https://windmill.dev https://windmill.dev provide an all-integrated developer platform with a Pg queue at its core that support jobs defined in python/typescript/sql
- mattbillenstein 3y agoI wrote one of these not long ago: https://github.com/mattbillenstein/pg-queue https://github.com/mattbillenstein/pg-queue
- tuananh 3y agoThere's also Riverqueue https://github.com/riverqueue/river https://github.com/riverqueue/river
- stavros 3y agoThere's a Dramatiq backend to do this (https://pypi.org/project/dramatiq-pg/ https://pypi.org/project/dramatiq-pg/) but I don't think it has seen as wide adoption as it deserves. I haven't tried it, but I don't see why it wouldn't work well.
- bbkane 3y agoDo you know if there's a MySQL dramatiq backend?
- cpursley 3y agoRelated post from a few days ago: “Postgres Is Enough”: https://news.ycombinator.com/item?id=39273954 https://news.ycombinator.com/item?id=39273954 Queue section in the gist: https://gist.github.com/cpursley/c8fb81fe8a7e5df038158bdfe0f06dbb#message-queues https://gist.github.com/cpursley/c8fb81fe8a7e5df038158bdfe0f...
- chuckhend 3y agosome notes about pgmq, https://github.com/tembo-io/pgmq https://github.com/tembo-io/pgmq, that is on this list. It is built as an extension in Postgres, which makes it compatible with all languages that have a Postgres driver. There's no 'magic' to it, it uses existing Postgres features so all the performance and consistency guarantees of Postgres are to be expected. Easily gets to 10k+ concurrent reads and writes even on smaller sized Postgres instances, which is more than most applications need.
- jmmv 3y agoI'm also using PostgreSQL as a queue of tasks for a side project and it has been OK---although it has barely enough load for me to say that it's "really OK" or whether it will turn out to be a problem in the future. I'm convinced I can go this way for a really long time without having to rearchitect the system or add more bloat to it. Anyhow, in my case, PostgreSQL is there just there to persist task state, and the database does very little magic. In fact, I also have a SQLite-backed implementation that allows service unit tests to be fast. Most of the work around managing such queue is in-process (and in Rust) because I wanted to keep deployments and operations simple. If interested, I wrote this thing a few months ago on how it works: https://jmmv.dev/2023/06/iii-iv-task-queue.html https://jmmv.dev/2023/06/iii-iv-task-queue.html
- throw__away7391 3y agoI am always surprised at how infrequently logical replication is mentioned when people are trying to implement these types of systems. I've used Debezium with some success in the past, but recently implemented my own application level reader to facilitate low latency event sourcing projections where I didn't want to involve Kafka. This seems to be quite a good solution in many scenarios that typically involve undesirable performance tradeoffs, as it doesn't introduce polling or much additional load on your DB, allows better isolation of the consumer workload, allows simple recovery if the consumer restarts or gets disconnected (e.g. you pick up at the LSN you left off and aren't worried that you missed a NOTIFY while offline), and watching status of the replication slot gives a since external metric to monitor of the health of the consumer.
- bodantogat 3y agoI like this approach, especially for systems that don't immediately require scaling to millions of users. I use MySQL with the 'SKIP LOCKED' option for my queues. I recognize that this method may not scale indefinitely, but it negates the need to add new infrastructure across all environments, saving on costs and devops effort. I've intentionally designed the event sending and receiving code to be abstract, ensuring that if the need to switch systems arises, it should be a reasonably manageable task.
- swaptr 3y agoI think it is a great idea if scaling is not an issue. I have been using Postgres backend on my side projects with celery and it works really well. Really handy if you wish to start off quick and on scale all you need to do is replace the backend/broker.
- throwawaaarrgh 3y agoThe idea that "performance" is the big problem, is the same as saying "performance" is why you don't use an IMAP server as a queue. Hey, you've already got an email server somewhere, and that means it's simpler, so that means it's a good idea. Based on the logic in this article you should be using your email server as your application's general purpose queue. I feel so bad for the poor engineers that will believe this crap and later regret it. If you're going to advocate doing something like this, you should be forced to be honest and explain all the reasons it's bad idea. The fact is that people are lying about the problems of using Postgres as a queue, because they just don't want to believe that the world is really more complex than that.
- sseagull 3y agoBut you aren’t storing application data in the IMAP server. I hope. The idea (for me anyway) is that if you have a postgres db with data, and need a queue for tasks dealing with that data, building it into the existing db can make more sense than adding additional infrastructure and complexity. Especially if you are familiar with postgres already. If all you need is a queue, then I agree that dedicated queue software will be better.
- throwawaaarrgh 3y agoThat's the fallacy. That keeping it all in one place with one tool is somehow better, because you can, or because it's all tightly coupled, or because it's one less piece of software. But in practice it just creates more problems (lots of them) that a dedicated, separate queue does not have. If you want a very simple way of looking at it, think about how code becomes modular. You don't put all your code in one function, or one class, or one file, or even one application. Not just because it's easier to read, but because specific components should have clearly defined delineations from others, and forceably be kept separate at the borders of their functionality. Doing that leads to benefits that outweigh them sitting next to each other, tightly tied into each other. At the very least, you should not be using one giant database for all your data in all your applications. Everybody should know that by now. And just like you shouldn't conflate different applications' data containers/interfaces/internals, you shouldn't conflate a queueing system with a completely separate data container. They need to be separate to avoid a bunch of problems. Again, not because you can't do it - like keeping all your code in a single function, or class - but because it's going to lead to a bad time.
- BrickTamblan 3y agoI’m not sure if this is the same as pg queues but I just started using pg_notify with channels, and it’s so good. Row update occurs as normal express api, but at the end I call pg_notify on some channel name. I pickup the message in a new thread via polling, and perform aggregate queries to a agg table. It’s like materialized views, but with no wasteful refreshes. Then I push the updates aggs back to the frontend via websockets.
- 698969 3y agoDoes the advice only apply to self-hosted/bare-metal VMs? Would sharing our Azure Flexible Postgres for a queue be a bad idea?
- KronisLV 3y agoOfftopic, but I added 1rem of left/right padding for paragraph and header elements in the stylesheet and personally it feels more readable that way.