Skip to content

Detect advisory locks at query_parser = auto on single-primary clusters - #1408

Open
mhenrixon wants to merge 1 commit into
pgdogdev:mainfrom
mhenrixon:fix/advisory-lock-disconnect-cleanup
Open

Detect advisory locks at query_parser = auto on single-primary clusters#1408
mhenrixon wants to merge 1 commit into
pgdogdev:mainfrom
mhenrixon:fix/advisory-lock-disconnect-cleanup

Conversation

@mhenrixon

Copy link
Copy Markdown

Fixes #1407.

Root cause

Debugging #1407 against a local build showed the leaked locks were a detection problem, not a cleanup problem. At query_parser = "auto" (the default), RegexParser::use_parser matches only the base session-control command set — CMD_RE, not CMD_RE_ADVISORY. On a cluster where nothing else engages the query parser (single primary, no sharding, no replicas — router_needed() is false), advisory lock statements therefore bypass the parser entirely:

  • the client is never marked locked, so it isn't pinned to its backend;
  • pg_advisory_unlock can route to a different server connection (Postgres emits a WARNING the app never sees) and the session lock strands on the pooled backend;
  • on abrupt client disconnect mid-hold, the server isn't flagged dirty, so the existing pg_advisory_unlock_all() cleanup never runs — [cleanup] no cleanup needed with AdvisoryLocks { locks: {} } in the debug log.

Sharded and read/write-split clusters are unaffected because the parser is already fully engaged for routing.

Fix

Match the advisory regex set (base + advisory patterns) at auto as well, so session advisory locks pin, unlock on the right backend, and get cleaned up on disconnect. The regex gate still only enables the per-request parse for matching statements, so plain traffic is untouched.

Verification

  • New unit test test_advisory_lock_auto_level (plus the existing advisory tests all pass).
  • End-to-end: local pgdog (transaction mode, single primary, default auto), client runs SELECT pg_try_advisory_lock(424242, 0) and is SIGKILLed while holding the lock.
    • Before: lock remains granted to the pooled backend indefinitely (pg_locks on the server).
    • After: pgdog logs [cleanup] running 3 cleanup queriesSELECT pg_advisory_unlock_all() and the lock is released.
    • Same repro with the client killed mid-query: the backend drains the running statement, then cleanup releases the lock.

At the auto level (the default), the regex fallback in
RegexParser::use_parser matched only the base session-control command
set. On clusters where nothing else engages the query parser — a single
primary, no sharding, no replicas — advisory lock statements therefore
bypassed the parser entirely: the client was never pinned to its
backend, pg_advisory_unlock could route to a different server
connection (the WARNING is silently swallowed), and the session lock
stranded on the pooled backend until it happened to be closed. Sharded
and read/write-split clusters were unaffected because the parser is
already fully engaged there.

Match the advisory regex set (base + advisory) at auto, so session
advisory locks pin, unlock correctly, and get cleaned up by
pg_advisory_unlock_all when a client disconnects mid-hold.

Fixes pgdogdev#1407
@CLAassistant

CLAassistant commented Aug 21, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session advisory locks leak into the pool when a client disconnects abruptly mid-hold (transaction mode)

2 participants