It was never the database: proving 11-second latency was cold starts
Abbas
August 3, 2026
An API backed by serverless Postgres was returning in 0.3 seconds sometimes and 11.5 seconds other times. Same endpoint, same parameters, same data.
Every instinct points at the database. Serverless Postgres, variable latency, a search query over ~15,000 rows β of course it's the query. Add an index, check the plan, tune the connection pool.
That would have been days of work on the wrong thing.
The measurement that settled it
Instead of optimising, I looked for a way to make the database irrelevant to the test.
The search endpoint accepts three named query parameters. If you call it with a parameter name that matches none of them, it returns an empty array before opening a database connection at all. That is a free experiment: an endpoint that provably runs zero SQL, on the same runtime, behind the same cold-start behaviour.
It returned in 6.5 seconds on first hit.
There is no query to tune. The time is spent before any query exists.
Confirming it wasn't a fluke
One measurement is an anecdote. I ran the same shape of test across four other endpoints that do no database work on their unhappy path:
| Request | First hit | Warm |
|---|---|---|
| zero-SQL endpoint A | 6.50 s | 0.30 s |
| zero-SQL endpoint B | 8.98 s | 0.32 s |
| zero-SQL endpoint C | 9.00 s | 0.34 s |
| zero-SQL endpoint D | 5.83 s | 0.31 s |
The pattern is unmistakable: first hit is seconds, warm is a third of a second, and the database is not in the picture on either side. This is function cold start β the platform spinning up a container, loading the bundle, and initialising the module graph before your handler runs.
Why this matters more than the fix
The fix for cold starts is a known menu: shrink the bundle, defer heavy imports until they're needed, avoid top-level work in modules, keep instances warm if the traffic justifies it, and move the region closer to your users.
But none of that menu is worth touching until you know it's the right menu. The reason this is worth writing down is the method, not the remedy:
To prove a component isn't responsible, find a code path that provably excludes it and measure that path.
It is the software equivalent of a control group. If the symptom persists with the suspect removed, the suspect is innocent β and you've saved yourself from tuning a query that was never slow.
The trap I nearly walked into
The seductive part is that the database would have looked guilty under investigation. Run an unindexed search on 15,000 rows and you will find something to improve. You would add the index, the numbers would improve slightly on warm requests, and you would declare victory while the 11-second first hits continued β because you fixed a real problem that wasn't the problem.
Measuring first is not a virtue. It is the only way to know which of the several real problems in your system is the one your users are actually feeling.
Commits b4822e1 and 2d7c8e2 in Insta Services.