Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion async_postgres/pg_advisory_lock.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@

import std/[macros, importutils]

import async_backend, pg_types, pg_connection, pg_client
import async_backend, pg_protocol, pg_types, pg_connection, pg_client

privateAccess(PgConnection)

Expand Down Expand Up @@ -89,14 +89,26 @@ template unlockSessionLock(
dec conn.heldSessionLocks
released

proc ensureXactScope(conn: PgConnection) {.inline.} =
# In tsIdle the acquire's implicit tx would commit and drop the lock before
# body runs, silently losing mutual exclusion.
if conn.txStatus != tsInTransaction:
raise newException(
PgStateError,
"transaction-level advisory lock requires an active transaction " & "(txStatus: " &
$conn.txStatus & "); wrap the call in withTransaction",
)

template acquireXactLock(
conn: PgConnection, sql: string, params: seq[PgParam], t: Duration
) =
ensureXactScope(conn)
discard await conn.queryValue(sql, params, timeout = t)

template tryXactLock(
conn: PgConnection, sql: string, params: seq[PgParam], t: Duration
): bool =
ensureXactScope(conn)
await conn.queryValue(bool, sql, params, timeout = t)

# Session-level exclusive locks
Expand Down
30 changes: 28 additions & 2 deletions tests/test_advisory_lock.nim
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,30 @@ suite "Advisory Lock: transaction-level (int64)":

waitFor t()

test "xact lock outside transaction raises PgStateError":
proc t() {.async.} =
let conn = await connect(plainConfig())
defer:
await conn.close()
doAssertRaises(PgStateError):
waitFor conn.advisoryLockXact(40010'i64)
doAssertRaises(PgStateError):
waitFor conn.advisoryLockXactShared(40011'i64)
doAssertRaises(PgStateError):
discard waitFor conn.advisoryTryLockXact(40012'i64)
doAssertRaises(PgStateError):
discard waitFor conn.advisoryTryLockXactShared(40013'i64)
doAssertRaises(PgStateError):
waitFor conn.advisoryLockXact(1'i32, 2'i32)
doAssertRaises(PgStateError):
waitFor conn.advisoryLockXactShared(3'i32, 4'i32)
doAssertRaises(PgStateError):
discard waitFor conn.advisoryTryLockXact(5'i32, 6'i32)
doAssertRaises(PgStateError):
discard waitFor conn.advisoryTryLockXactShared(7'i32, 8'i32)

waitFor t()

suite "Advisory Lock: two-key (int32, int32)":
test "lock and unlock":
proc t() {.async.} =
Expand Down Expand Up @@ -228,8 +252,9 @@ suite "Advisory Lock: two-key (int32, int32)":
await conn2.close()
conn1.withTransaction:
await conn1.advisoryLockXactShared(9'i32, 10'i32)
let acquired = await conn2.advisoryTryLockXactShared(9'i32, 10'i32)
let acquired = await conn2.advisoryTryLockShared(9'i32, 10'i32)
doAssert acquired
discard await conn2.advisoryUnlockShared(9'i32, 10'i32)

waitFor t()

Expand Down Expand Up @@ -392,8 +417,9 @@ suite "Advisory Lock: withAdvisoryLockXact template":
await conn2.close()
conn1.withTransaction:
conn1.withAdvisoryLockXactShared(17'i32, 18'i32):
let acquired = await conn2.advisoryTryLockXactShared(17'i32, 18'i32)
let acquired = await conn2.advisoryTryLockShared(17'i32, 18'i32)
doAssert acquired
discard await conn2.advisoryUnlockShared(17'i32, 18'i32)

waitFor t()

Expand Down