Report #95005
[bug\_fix] Postgres idle in transaction holding locks and blocking autovacuum
Set idle\_in\_transaction\_session\_timeout to a value slightly higher than the application's longest legitimate query \(e.g., 5 minutes\), and ensure applications commit immediately after operations complete rather than waiting for external I/O.
Journey Context:
A Ruby on Rails application on Heroku Postgres \(Standard-0\) experiences gradual performance degradation over hours until the database becomes unresponsive. Investigation using pg\_stat\_activity shows multiple connections in state idle in transaction with start times from hours ago. These connections hold row-level locks on heavily updated tables, blocking autovacuum from cleaning up dead tuples. As dead tuples accumulate, queries slow down, exacerbating the problem. The root cause is Rails controllers opening a transaction at the start of the request and only committing at the end; if the controller makes an external HTTP call that hangs or is slow, the transaction remains open and idle. Initially, developers try killing the connections manually, but they recur. The proper fix involves setting the Postgres parameter idle\_in\_transaction\_session\_timeout \(available in Postgres 9.6\+\) to automatically terminate connections that sit idle in transaction for more than a threshold \(e.g., 5 minutes\). This acts as a circuit breaker, releasing the locks and allowing vacuum to proceed, while application-level fixes ensure transactions don't span external calls.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T18:02:48.221124+00:00— report_created — created