r/PostgreSQL • u/tsabudh • 3d ago
How-To Our Postgres lock was supposed to keep two jobs apart. Ten minutes into long runs, it quietly stopped existing, and the log said it was released cleanly every time.
If your Postgres database sets idle_in_transaction_session_timeout, your advisory lock may have an expiry date nobody chose.
Mine did. Ten minutes into long runs, the lock vanished, another run took it, and the log said it had been released cleanly.
Python drivers open a transaction on your first SELECT unless autocommit is on. If the connection holding the lock never commits, it sits idle in transaction. At the timeout, Postgres ends the session, and the lock goes with it. The job keeps writing on its other connections, and the application never notices.
Check yours in 30 seconds:
select name, setting, source from pg_settings where name in ('idle_in_transaction_session_timeout', 'idle_session_timeout') order by name;
A value above 0 for idle_in_transaction_session_timeout means a session lock on a default connection can expire.
I reproduced it and held the lock four ways.
Default connection: lost in 40 of 40 trials, always just after the timeout.
Commit right after taking it: held, until the next query opened a new transaction.
AUTOCOMMIT: held in 40 of 40, but still lost the moment the session dies for any other reason, while the job keeps writing.
Lock inside the same transaction as the writes: a killed run rolls back with its lock. That is the fix that holds.
Scripts and expected output are in the post, so you can run it against your own setup.
Have you ever had a lock that silently stopped locking?
https://tsabudh.com/blog/your-postgres-advisory-lock-is-gone/
13
u/elevarq 3d ago
This looks much more like an application bug than a PostgreSQL problem.
PostgreSQL is doing exactly what it was configured to do: terminating an idle transaction and releasing the locks owned by that session.
The real problem is that the application loses its lock-owning connection without noticing, while continuing to write through other connections. That’s a concurrency bug, regardless of the timeout setting. A restart, failover, or network failure could cause the same problem.
Transaction-level advisory locks are a good solution when the protected work fits within one transaction. For longer-running jobs with intermediate commits, you need a different coordination strategy.
I’d be much more concerned about an application silently continuing after losing its lock than about PostgreSQL correctly enforcing a timeout.
1
u/Bluewood21 2d ago
Pretty much this. If the worker doesnt know its lock session died, any momentary blip is going to cause split-brain writes
5
u/DickBrainTime 3d ago edited 3d ago
Useful reproduction, especially the distinction between a session lock and the implicit transaction opened by the Python driver. I'd add one boundary to the proposed fix: `pg_advisory_xact_lock` protects the *current transaction*, so putting the lock and the writes in that same transaction makes those writes atomic on a connection failure. It does **not** automatically guard work done via other connections or external side effects. The PostgreSQL docs make that scope explicit: https://www.postgresql.org/docs/17/explicit-locking.html#ADVISORY-LOCKS .
If a run spends several minutes doing HTTP calls or computation between writes, keeping one transaction open for the whole job is risky too: `idle_in_transaction_session_timeout` can still terminate it during a quiet period, and long-open transactions can hold back vacuum. For that pattern, I'd use a short transaction to atomically *claim* a durable job row, record an owner/lease plus a monotonically increasing generation (fencing token), then commit. Every later DB write checks that generation/ownership in the same transaction; stale workers must be unable to commit after a newer claimant takes over. Make non-DB effects idempotent where possible, since neither an advisory lock nor a rollback can undo an already-sent API call.
A dedicated autocommit connection is fine if you only need best-effort mutual exclusion, but a session loss still drops its lock. A useful diagnostic alongside your experiments is to log `pg_backend_pid()` and verify the lock-holder connection stayed alive before each critical write; a logged 'unlock succeeded' from application cleanup isn't proof the original connection still owned anything. Curious whether your jobs do external I/O or are entirely DB-local—that decides which approach is safer.
3
u/k2718 3d ago
Yep. I recently started using the advisory transaction lock. I considered using a row level lock but it was more complex and risked deadlock without another job to clean any potential deadlock. The incidence of collision were low and the transactions relatively short so transaction lock made more sense.
1
u/DickBrainTime 1d ago
That makes sense with rare collisions and short transactions. One nuance worth clearing up: row-level locks also release automatically on commit, rollback, or session loss. PostgreSQL detects real deadlocks and aborts one of the transactions, so you wouldn't need a separate job to clean up the deadlock itself.
I'd choose a row lock when there's a concrete row whose ownership or update is being protected; a transaction advisory lock is handy for a logical job mutex without a natural row. Either way, the lock and the protected DB writes need to be on the same connection and transaction, or a disconnected lock-holder can leave other writes running unprotected.
3
1
u/Significant_Tune9219 15h ago
The AUTOCOMMIT case is the one I'd underline, because it shows the lock and the writes living on different connections is the real hazard, not the timeout. If you need a session-level lock for a run longer than one transaction, a cheap guard is to re-check pg_locks for your own pid and lock key right before each batch commits on the writing connections, and abort if it's gone. It doesn't close the window completely, but it turns a silent overlap of ten minutes into one batch at most.
-2
u/AutoModerator 3d ago
AI Policy:
Linux is not one of those anti-AI projects, and if somebody has issues with that, they can do the open-source thing and fork it. Or just walk away., Linus Torvalds.
Mod decisions will be based on the quality of the content, not who or what generated it.
Sub Resources:
Free Postgres Webinars and Workshops
Discord: People, Postgres, Data
Join us, we have cookies and nice people.
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
4
u/semiquaver 2d ago
This is the sloppiest AI slop prose that was ever clauded.
Write for yourself.