A fast database does not give you a fast API. Mostly it moves the slow part into a place you are not measuring.
Query time is service time. Response time is service time plus queue time, and queue time is where APIs actually die.
The mechanics are boring and consistent. A synchronous worker pool has a fixed number of slots. A request holds one slot for its entire lifetime: waiting for a database connection checkout, waiting on an outbound call to a carrier or a payment provider, serializing a large response, writing bytes to a slow client. None of that is query time. All of it occupies a worker.
When the arrival rate gets close to what the pool can drain, requests start waiting for a slot before any work begins. Your database dashboard stays green, because every individual query really is fast. The queue sitting in front of the handlers is not on that dashboard.
This is also why one heavy endpoint degrades endpoints it shares no tables with. A report that pins workers for seconds starves the checkout path next to it. Head-of-line blocking, not contention.
What I do about it: measure the gap between a request arriving at the proxy and the handler starting. That single number tells you whether you have a database problem or a concurrency problem. Then size the connection pool to the worker count rather than to the database maximum, give long-running endpoints their own pool or move them to a background queue, and put a timeout on every outbound call so one stalled dependency cannot hold slots indefinitely.
Summing query durations will not reproduce your p99. Latency under load is a queueing property, not a storage property.
If you chased a slow endpoint recently, I'd want to know where the time actually turned out to be: the database, an outbound dependency, or waiting for a worker...