Locks & Blocking
Locking is how Postgres ensures data consistency, but too much locking (contention) leads to blocking, where queries wait indefinitely for others to finish.
How blocking leads to timeouts
When a "heavy" query (like a migration or a massive update) holds a lock, other queries trying to access the same data will queue up. Eventually, your app will hit a timeout, and users will see errors.
What to check
- Long Transactions: A transaction that stays open for minutes while the app does something else (like calling an external API).
- Schema Migrations: Operations like
ALTER TABLEoften require exclusive locks. - Background Jobs: Heavy reports or batch updates running during peak traffic.
What to do
- Identify the Blocker: Use pgpulse to see which query is currently holding the lock that others are waiting for.
- Terminate Cautiously: In an emergency, you can terminate the blocking backend, but do so with caution to avoid data inconsistency.
- Reduce Transaction Time: Keep transactions as short as possible. Never perform network calls or heavy logic inside a database transaction.
- Use
CONCURRENTLY: When creating indexes, always useCREATE INDEX CONCURRENTLYto avoid locking the table for writes.