Skip to content
Kon Fartusov
← All notes

We replaced a managed queue with 200 lines of Postgres

postgresqueuesinfrastructure

Every job system starts the same way: you need work to happen later, somewhere else, and the internet tells you to buy a queue. We bought one, ran it for two years, then took it out.

What the managed queue actually gave us

Three things, and only one of them mattered.

  • Delivery guarantees we could have written ourselves in an afternoon.
  • A dashboard nobody opened after the first month.
  • A second system to operate, with its own outages, its own SDK and its own bill.

What replaced it

One table, one index, and a locked_until column standing in for lease renewal:

SELECT * FROM job_queue
 WHERE queue = $1 AND status = 'queued' AND run_at <= now()
 ORDER BY run_at
 FOR UPDATE SKIP LOCKED
 LIMIT 1;

FOR UPDATE SKIP LOCKED is the whole trick. Postgres hands each worker a different row and never blocks one behind another. LISTEN/NOTIFY wakes idle workers so nothing polls in a tight loop.

The thing that made this safe was not the SQL. It was writing 30 behavioural tests against the old queue first, then making the new one pass the same tests.

What it cost

Two weeks of evenings, most of it spent on tests rather than on the queue. What we got back was one less vendor, one less bill, and a system where a failing job is a row you can read.

Not every managed service deserves this treatment. This one did, because the surface we actually used was smaller than the client library we imported to use it.