The endpoint ran 1 database query. It still took three seconds.
That's exactly why it hid for months. I was watching query counts, and the count was spotless: one lookup, one object out. No slow query log, no missing index, nothing to blame.
The cost sat in the serializer. This was a deal listing on a high-ticket asset marketplace, and every listing carried a small completeness badge (whether the deal's documents were signed and current). To compute that badge, the serializer asked the document service about each document in turn. Twenty-something calls per listing, each one fast, all of them over the network, all of them sequential. Nobody wrote a loop that looked like a loop. It was a property on a nested field.
Two changes took it to about 200ms: one batched call in place of the per-document calls, and a narrower response contract, because the client rendering the list never needed the full document set. It needed a flag.
What I do differently now: I instrument outbound calls per request the same way I instrument database queries. An N+1 inside a process is noisy and easy to catch. An N+1 across a service boundary is polite and well-typed, and invisible to every counter you already trust.
I write up more of these in long form on my blog: https://polycratia.com/c/ec39f919
Where your request-level outbound calls actually get counted, if they get counted at all...