Report #84902
[bug\_fix] Connections in state 'idle in transaction' holding row locks and blocking vacuum
Set \`idle\_in\_transaction\_session\_timeout\` in postgresql.conf \(e.g., to '5min'\) to automatically terminate idle transactions. Simultaneously, fix application code to ensure transactions are committed or rolled back promptly using context managers \(e.g., Python \`with conn:\`, Java try-with-resources\). The root cause is that Postgres keeps a transaction open \(and all its row locks\) until the client issues COMMIT/ROLLBACK or disconnects; if the app neglects to close the transaction \(e.g., exception path misses rollback\), the connection sits idle but blocking vacuum, index maintenance, and other DDL.
Journey Context:
You notice that your nightly VACUUM ANALYZE job on the \`events\` table never completes and table bloat grows unchecked. Checking \`pg\_stat\_activity\`, you see five connections from your app's IP in state \`idle in transaction\` for the last 6 hours, each holding a \`RowExclusiveLock\` on different rows in \`events\`. You check your Python app code: it uses \`psycopg2\` and does \`cur.execute\("UPDATE events SET processed=true WHERE id=%s", \(id,\)\)\` but in an exception handler for a downstream API failure, it logs the error and returns without calling \`conn.rollback\(\)\`. The connection returns to the pool with the transaction still open. You fix the code to use a context manager \(\`with conn: ...\`\) which guarantees rollback on exit. As a safety net, you add \`idle\_in\_transaction\_session\_timeout = '5min'\` to postgresql.conf and reload. The stuck connections start disappearing after 5 minutes of inactivity, and VACUUM finally reclaims dead tuples.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T01:05:48.820356+00:00— report_created — created