In production a balance endpoint in a custodial wallet service started timing out, and the slow query log had nothing in it.
Every query it ran was milliseconds. Hit the service on its own and it answered instantly. Under real traffic it took seconds.
The handler was async. One call inside it wasn't: a node RPC through a client that blocked the thread. One request doing that is invisible. Ten requests doing it, and the last one spends almost its entire life queued behind the other nine, having done no work of its own yet.
So the number I was staring at wasn't the cost of that request. It was everyone else's cost, billed to whoever showed up last.
The part that actually hurt: nothing in the wallet broke, but everything sharing that worker got slow too, including the payment callback endpoint. Callbacks started timing out on the sender's side and retrying. A read-path mistake turned into duplicate write attempts on the money path.
What I do differently now: I measure two numbers per request instead of one. Time from arrival to the handler actually starting, and time spent inside the handler. If the first grows with traffic while the second stays flat, none of my code is slow: my concurrency is a lie.
And any blocking client in an async service I treat as a shared resource, not as a local implementation detail.
I wrote up how I separate wait time from service time here: https://polycratia.com/c/1721163e
Mine was hiding in an RPC client. Yours is probably sitting in a driver, a vendor SDK, or your own logging...