PostgreSQL transactions block unexpectedly when mixing SERIALIZABLE snapshot reads and FOR UPDATE in prepared statements
Environment
PostgreSQL 13.5 on Ubuntu 20.04, application server using libpq with prepared statements (persistent prepared transactions), workload: web app that uses a read step (SELECT ... FOR SHARE) under SERIALIZABLE isolation and later issues SELECT ... FOR UPDATE in a follow-up prepared statement in the same logical transaction.
Symptoms
Certain user operations hang indefinitely during peak load; stuck backend processes show an open transaction holding a tuple lock. Other transactions attempting to update the same rows wait and do not time out. Manual inspection shows prepared statements created by the application are executing the FOR UPDATE step but are blocked on a lock held by an earlier transaction that had previously executed a SERIALIZABLE snapshot read.
Observations
ps aux shows application backend processes waiting in 'idle in transaction' state. pg_locks reveals a tuple lock held by a session that performed a SERIALIZABLE SELECT; its xmin and snapshot_xmin indicate an old snapshot. Executing SELECT pg_prepared_statements() shows many prepared statements with identical SQL text (placeholders) and different parameter sets. Statement_timeout is set globally to 30s but the app's connection pool sets it to NULL for some connections. The blocked queries do not produce deadlock errors — they simply wait. VACUUM activity and autovacuum are normal.
Already attempted
Restarted the app to clear prepared statements (temporary improvement). Increased statement_timeout and idle_in_transaction_session_timeout in a staging environment which caused blocked transactions to fail faster. Reviewed ORM configuration and found it uses server-side prepared statements by default and sometimes reuses a connection with older snapshot state. Tried setting default_transaction_isolation to READ COMMITTED on a test replica which removed hangs for the same load pattern.
Question
How can mixing SERIALIZABLE snapshot reads and later FOR UPDATE operations in prepared statements cause persistent blocking, and what application- or server-side mitigations (configuration or code changes) will avoid long waits without altering transaction semantics? What diagnostic steps isolate whether prepared-statement plan caching or snapshot lifetime is the primary cause?
Verified community troubleshooting
No answers yet..
Incident analysis
Most likely causes
Summary
Suggested Fixes
Continue troubleshooting
Related troubles
Matched from the title, description and tags
- Databases32% match
JDBC prepared statements on application server suddenly select a generic plan causing a 15× slowdown for partitioned table queries
Open0 answers - Databases28% match
PostgreSQL prepared statements pick a generic plan after ANALYZE on a partitioned table, causing 10× slower execution
Open0 answers - Databases27% match
JDBC clients fail with 'bind message supplies 0 parameters' after ALTER COLUMN type; prepared statements misbehave on PostgreSQL 14
Open0 answers - Databases23% match
PostgreSQL query becomes slow only with prepared statements
Open1 answer - Databases22% match
PostgreSQL 14: lo_export/large object operations intermittently fail with 'could not open large object' when keepalives drop during long client transactions
Open0 answers