query-timeout-diagnosis
Installation
SKILL.md
Query timeout diagnosis
A specific playbook under the performance skill: its measured before and after still applies.
Use when a Node/pg app reports query timeouts (Query read timeout, bounded-operation deadline errors) but DB metrics look healthy.
Decision tree
- Get the real error source.
Error: Query read timeoutatpg/lib/client.jsis the client-sidequery_timeout(wall clock).canceling statement due to statement timeoutis server-side. Drizzle'sDrizzleQueryError("Failed query: ") hides the pg cause; find the run trace or reproduce to get it. - Check DB health during the failure window (CPU, active connections, error logs). If CPU is low, connections are far under max, and there are no DB errors, the database is innocent: do NOT raise DB resources.
- Ask which statement timed out. BEGIN, COMMIT or a single-row indexed UPDATE "taking 30s" is physically impossible server-side, so the time was spent client-side.
- Client-side causes, in order of likelihood:
- Event-loop stall. pg's
query_timeoutcounts wall time until the event loop reads the socket. CPU-heavy work (JSON parse or stringify of large payloads, tokenization) on a small worker (Trigger.devmicrois 0.25 vCPU) stalls the loop past the timer even though Postgres answered in milliseconds. Check the worker machine preset and the compute duration. - Per-row query loops over WAN. N sequential round trips (about 50-100ms each) inside one bounded transaction: 400+ rows is roughly 30s or more. Grep the failing helper for
for (...) await query(...). - Pool acquisition starvation. An unbounded
Promise.allof N queries through a small pool (max: 10) with a shortconnectionTimeoutMillis. Concurrent workloads multiply it.
- Event-loop stall. pg's