T0ha666

T0ha666

The GenServer is a cache, Postgres is the queue — why my agent queue survives deploys

I run a kanban of AI coding agents, and one afternoon the board showed three tasks running while the profile page on the same screen said two running and one queued. Both were correct: one card was waiting in line, and nothing in the interface said so. That UI bug turned out to sit on top of a design decision I think is worth sharing.

An agent run costs tokens and a container with a real toolchain, so concurrency has to be capped. The obvious way is to refuse work when you are full and let every caller retry. I did not want that: the pool always accepts a dispatch and makes the session wait its turn. But a queue that never rejects has to live somewhere — and a GenServer holding a :queue in its state is gone the moment you deploy. I deploy several times a week, so every deploy would silently drop every queued session. Not crash, just drop.

So the queue lives in Postgres, as Session rows with status: :queued, and the GenServer state is a fast index over those rows, rebuilt on boot by a reconciler. The state still does real work — a per-user :queue map for O(1) pops, a MapSet of active sessions for cap checks, waiter pids, monitor refs — but none of it is information you could not reconstruct. Delete the whole struct, replay the rows, and you are back where you were.

The one OTP feature I actually rely on: every waiter is monitored on enqueue, and handle_info({:DOWN, ...}) releases its slot and dispatches the next in line. Inside the VM, “notice when the thing holding a resource dies” is four lines, and they are exactly right.

Two rules I took out of it: if it must survive a restart, it is a row — kill the process and ask what was lost. And a queue that never rejects has to be visible, or your users think the app is broken while it works exactly as designed.

Full write-up with the code:
https://t0ha.dev/en/elixir/2026/10/29/genserver-is-a-cache.html

Where do you keep your queues — have you ever lost one to a deploy, or do you just refuse work when you are full?

First Post!

aicost_tools

aicost_tools

“If it must survive a restart, it is a row” is the line I’m stealing. The visibility point matters even more with agents than with normal jobs: a queued session that looks stuck tends to get re-dispatched by an impatient user, and then you pay for the same tokens twice. (Disclosure: I’m with aicost.tools, so double-billed agent runs are my pet peeve.) Do you dedupe dispatches on the Postgres side, e.g. one queued session per task, or did the UI fix make that unnecessary?

Where Next?

Popular Backend topics Top

First poster: bot
Taking PHP Seriously - Slack Engineering. Slack uses PHP for most of its server-side application logic, which is an unusual choice these...
New
First poster: bot
When I need to configure something in a complicated way, I find myself reviewing the embedded language that provided the server to create...
New
First poster: bot
We all know how to teach recursion. We’ve done it for decades. We pick some honored, time-tested examples—Fibonacci numbers and factorial...
New
AstonJ
If you’re interested in Rust this is worth a read :smiley: Technology from the past come to save the future from itself Hi I have be...
New
First poster: bot
What’s Next for Teal, the typed dialect of Lua - FOSDEM 2021. This is my talk about the latest updates on the Teal programming language,...
New
AstonJ
Not had time to read it yet but this looks like a good interview… Our friend Yukihiro Matsumoto, creator of the Ruby programming langua...
New
First poster: bot
The run-time speed and memory usage of programs written in Rust should about the same as of programs written in C, but overall programmin...
New
First poster: bot
Too long have we hustled to deploy Clojure websites. Too long have we spun up one server instance per site. Too long have reminisced abou...
New
ragamuf
Does the world need another How to create a blog article? Maybe not. But then again, creating something out of nothing is what we love....
New
brainlid
Episode 264 of Thinking Elixir. News includes Phoenix v1.8 shipping with an AGENTS markdown file for new apps to enhance coding agent exp...
New

Other popular topics Top

PragmaticBookshelf
Machine learning can be intimidating, with its reliance on math and algorithms that most programmers don't encounter in their regular wor...
New
DevotionGeo
I know that -t flag is used along with -i flag for getting an interactive shell. But I cannot digest what the man page for docker run com...
New
mafinar
This is going to be a long an frequently posted thread. While talking to a friend of mine who has taken data structure and algorithm cou...
New
AstonJ
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
PragmaticBookshelf
Build efficient applications that exploit the unique benefits of a pure functional language, learning from an engineer who uses Haskell t...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
PragmaticBookshelf
Programming Ruby is the most complete book on Ruby, covering both the language itself and the standard library as well as commonly used t...
New
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New
PragmaticBookshelf
As digital systems increasingly run the world, mastery of the recurring patterns of software development risk is the key to fast and effe...
New
pragdave
Build robust LLM-powered apps, chatbots, and agents while mastering AI engineering principles that will help you outlast the tools and th...
New