Report #9706
[bug\_fix] Postgres idle in transaction holding locks and exhausting connection pool
Set idle\_in\_transaction\_session\_timeout to automatically terminate abandoned transactions, and ensure application code commits or rolls back transactions immediately after database operations complete, without holding them open during I/O.
Journey Context:
Production application gradually degrades over several hours, eventually returning 500 errors with "sorry, too many clients already". Checking pg\_stat\_activity shows 95 of 100 connections are in state 'idle in transaction', some with state\_change timestamps from 3 hours ago. These connections hold row-level locks \(visible in pg\_locks\) preventing vacuum and causing bloat. Investigation reveals a background job that opens a transaction, queries for unprocessed orders, then calls an external HTTP API for each order before committing. When the external API experiences high latency \(30s timeouts\), the transaction remains open holding locks, but the connection is idle from Postgres's perspective \(no query running\). The fix has two parts: immediate relief comes from setting idle\_in\_transaction\_session\_timeout = '5min' in postgresql.conf, which automatically terminates any backend idle in transaction for more than 5 minutes, rolling it back and releasing locks; architectural fix requires restructuring the application to fetch data, close the database transaction, perform external HTTP calls, then open a new transaction only to update the database, ensuring transactions never span I/O operations.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T08:50:20.755198+00:00— report_created — created