Skip to content

Failed advisory unlock can return a still-locked session to pgxpool #19

Description

@ewhauser

Summary

In v0.3.0, Observation.CloseContext returns its dedicated pgx connection to the pool even when releasing the session-level advisory lock fails:

err := dbgen.New(o.conn).ReleaseEntitySessionLock(ctx, o.key)
o.conn.Release()
if err != nil {
    return fmt.Errorf("release observation lock %s: %w", o.key, err)
}

Because PostgreSQL advisory locks are session-scoped, an unlock failure means the physical session may still own the lock. Returning that connection to pgxpool can preserve the lock indefinitely and allow a later borrower to inherit the locked session.

The fetch handlers also invoke CloseContext through deferred calls whose errors are intentionally ignored, so the cleanup failure is not surfaced operationally.

Reproduction

  1. Begin an observation so that a dedicated connection owns a session-level advisory lock.
  2. Cause ReleaseEntitySessionLock to fail or exceed the five-second cleanup timeout.
  3. Call CloseContext and observe that it returns an error after calling conn.Release().
  4. Inspect pg_locks or reacquire the same pooled session and observe that the advisory lock can remain owned.

Expected behavior

A connection whose session lock could not be released must not be returned to the pool. The physical connection should be discarded or closed, and the cleanup failure should be observable through logging, tracing, or metrics.

Actual behavior

The connection is unconditionally returned to the pool before the unlock error is handled, and callers discard the returned error.

Possible fix

  • On unlock failure, destroy or close the physical connection instead of releasing it back to the pool.
  • Return the connection normally only after a confirmed successful unlock.
  • Surface deferred cleanup failures through a bounded log, trace event, or metric.
  • Add a regression test that forces unlock failure and verifies that no reusable pooled session retains the advisory lock.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions