跪拜 Guibai
← All articles
Backend · MySQL · SQL

A Layer-by-Layer Guide to Diagnosing API Timeouts Without Guessing

By 云技纵横 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Timeout incidents routinely waste hours as teams blame the network, the database, or the code without evidence. A repeatable, layer-by-layer checklist turns a multi-team argument into a 15-minute forensic exercise and prevents the common mistake of masking a slow database with bigger thread pools.

Summary

When an API times out, the instinct to restart or blindly expand pools often destroys the evidence and pushes pressure downstream. A more reliable approach follows the request path from the outside in, answering one question at each layer: did the request arrive, and how long did it stay?

The diagnostic sequence starts with a curl command to separate client-side from server-side latency. Nginx access logs enriched with upstream timing variables then reveal whether the connection to the application was refused, established slowly, or established quickly but followed by a long wait for response headers. Inside the JVM, three consecutive thread dumps pinpoint whether Tomcat threads are blocked waiting for a HikariCP connection, stuck on a socket read, contending for a lock, or genuinely CPU-busy.

At the connection pool layer, four numbers tell the story: active, idle, pending, and max. A pool at maximum with a growing pending queue confirms congestion, but the root cause is further down — slow SQL, oversized transactions, or remote calls inside transactions. MySQL's process list and Performance Schema queries then distinguish between long-running statements and sessions waiting on locks, guiding the final step of checking execution plans or identifying blocking transactions.

Takeaways
Start every timeout investigation with a curl command that captures connect, starttransfer, and total times to separate client-side from server-side latency.
Add upstream timing variables to Nginx access logs: a low uct with a high uht means the connection reached the app but no response header came back.
Three consecutive Java thread dumps are more reliable than a single snapshot; threads stuck at the same call across all three dumps indicate a persistent wait point.
Tomcat threads blocked on HikariPool.getConnection() point to connection pool exhaustion, not necessarily a database problem.
A HikariCP pool with active=max and pending>0 confirms congestion, but the fix is finding why connections aren't returning — not just increasing max.
Use MySQL's Performance Schema events_statements_current to find long-running SQL and check for NO_INDEX_USED before running EXPLAIN.
Never expand thread pools, connection pools, or timeouts before identifying the actual bottleneck; doing so just moves pressure to the next layer.
Preserve Nginx logs, thread dumps, connection pool metrics, and MySQL session lists before restarting anything.
Conclusions

The diagnostic sequence is deliberately ordered by proximity to evidence: Nginx logs are always available, thread dumps require JVM access, and MySQL queries need database credentials, so starting from the edge avoids wasted context-switching.

Status code 499 is widely misunderstood as an Nginx problem when it often just means the client's timeout budget was smaller than the server's response time — a mismatch in timeout configuration across layers.

The advice to capture three thread dumps rather than one is underappreciated. A single dump can mislead by catching a thread in a transient state; repetition filters noise.

Connection pool exhaustion is a symptom, not a diagnosis. The article correctly frames it as a congestion indicator that forces the next question: what is holding the connections?

EXPLAIN ANALYZE runs the actual query, which makes it dangerous on production write paths or large tables — a caveat many guides omit.

Concepts & terms
Nginx upstream timing variables
Custom log fields — upstream_connect_time, upstream_header_time, upstream_response_time — that measure how long Nginx spends connecting to a backend and waiting for headers, separating network latency from application latency.
HikariCP pool metrics (active, idle, pending, max)
Four counters that describe connection pool health: active connections in use, idle connections available, threads waiting for a connection (pending), and the configured maximum pool size.
Nginx 499 status code
A non-standard Nginx code indicating the client closed the connection before the server responded, often caused by a client-side timeout shorter than the server's processing time.
Performance Schema events_statements_current
A MySQL Performance Schema table that shows currently executing SQL statements with timing, rows examined, and index usage, providing more detail than SHOW PROCESSLIST.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗