Here is the entire state management of project #1:
let cookiesInJar = 12;On your laptop this is perfect. You add twenty cookies, you look in the jar, there are thirty-two. Every time.
Deploy it to Vercel and it starts telling lies.
Many machines, many copies, all asleep
Vercel runs your code on however many machines it feels like, spun up and torn down on demand. Each one gets its own copy of that variable.
And it is worse than "two machines disagree", because machines also fall asleep. A function that has been idle gets frozen or discarded. Come back twenty minutes later and the count has silently reset to 12 with no request having touched it.
Memory in a serverless function is a sandcastle β real, working, and taken by the tide.
The bug is the curriculum
This was left in deliberately, and the README says so out loud rather than quietly shipping something that works.
Project #3 fixes it, with the same tool, the same protocol and the same answers β the count just lives in Postgres now. And the interesting part of that fix is what didn't change.
The model cannot tell the difference
That is the payoff, and it is worth sitting with for a second.
cookie_jar in project #1 and cookie_jar in project #3 present an identical
interface: same name, same description, same schema, same shape of reply. The
model calls it and gets a number back. It has no idea whether that number came
from a variable, a database, or a piece of paper.
The fix, in one statement
For completeness, since project #3 does it and it's smaller than you'd expect:
update jar_state set cookies = cookies - $1
where id = 'default' and cookies >= $1
returning cookiesOne statement. Read and write together, atomic because it is indivisible β so two people eating cookies at the same instant cannot lose one, and the "not enough cookies" check cannot race the write either.
The and cookies >= $1 guard is doing two jobs: it is the business rule and
the concurrency control. Splitting them into a select then an update would
reintroduce both bugs at once.
That, and the reason serverless code needs a driver that doesn't hold connections, is on Pausing is an array.