Skip to content

Commit d813ce5

Browse files
committed
Describe Postgres lock drops
1 parent c9788c2 commit d813ce5

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

src/locks/postgres.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ impl Lock for PostgresLock {
111111
.context("Failed to acquire connection for shared lock")?;
112112

113113
// Acquire shared advisory lock (returns void, so we use query instead of query_scalar)
114+
// PostgreSQL advisory locks are session-scoped and automatically released when the
115+
// connection is closed or returned to the pool.
114116
sqlx::query("SELECT pg_advisory_lock_shared($1)")
115117
.bind(self.key_hash)
116118
.execute(&mut *conn)
@@ -119,6 +121,10 @@ impl Lock for PostgresLock {
119121

120122
debug!("Acquired shared lock for key: {}", self.key);
121123

124+
// Return a guard that holds the connection. When the guard is dropped, the
125+
// connection is automatically returned to the pool, which triggers the lock release.
126+
// This design is safe and reliable - it leverages PostgreSQL's guarantee that
127+
// locks are released when a connection closes, without needing explicit unlock calls.
122128
Ok(Box::new(PostgresSharedLockGuard {
123129
key: self.key.clone(),
124130
_conn: conn,
@@ -136,6 +142,8 @@ impl Lock for PostgresLock {
136142
.context("Failed to acquire connection for exclusive lock")?;
137143

138144
// Acquire exclusive advisory lock (returns void, so we use query instead of query_scalar)
145+
// PostgreSQL advisory locks are session-scoped and automatically released when the
146+
// connection is closed or returned to the pool.
139147
sqlx::query("SELECT pg_advisory_lock($1)")
140148
.bind(self.key_hash)
141149
.execute(&mut *conn)
@@ -144,13 +152,35 @@ impl Lock for PostgresLock {
144152

145153
debug!("Acquired exclusive lock for key: {}", self.key);
146154

155+
// Return a guard that holds the connection. When the guard is dropped, the
156+
// connection is automatically returned to the pool, which triggers the lock release.
157+
// This design is safe and reliable - it leverages PostgreSQL's guarantee that
158+
// locks are released when a connection closes, without needing explicit unlock calls.
147159
Ok(Box::new(PostgresExclusiveLockGuard {
148160
key: self.key.clone(),
149161
_conn: conn,
150162
}))
151163
}
152164
}
153165

166+
/// Guard for a PostgreSQL shared advisory lock.
167+
///
168+
/// This guard holds a database connection from the connection pool. When the guard is dropped
169+
/// (goes out of scope), the connection is automatically returned to the pool. PostgreSQL
170+
/// automatically releases all advisory locks held by a connection when that connection is
171+
/// closed or returned to the pool.
172+
///
173+
/// Lock Release Mechanism:
174+
/// 1. Guard is created in acquire_shared() with the connection acquired from the pool
175+
/// 2. PostgreSQL advisory lock is held as long as the connection is active
176+
/// 3. When guard is dropped, the connection goes out of scope and is returned to the pool
177+
/// 4. PostgreSQL automatically releases the lock when the connection returns to the pool
178+
///
179+
/// This design is superior to explicit unlock calls because:
180+
/// - No risk of forgetting to unlock (guaranteed by Rust's Drop trait)
181+
/// - No fire-and-forget async tasks that might fail to execute
182+
/// - Lock release is atomic with connection return
183+
/// - Works correctly even if the runtime is shutting down
154184
struct PostgresSharedLockGuard {
155185
#[allow(dead_code)]
156186
key: String,
@@ -160,6 +190,24 @@ struct PostgresSharedLockGuard {
160190

161191
impl<'a> SharedLockGuard<'a> for PostgresSharedLockGuard {}
162192

193+
/// Guard for a PostgreSQL exclusive advisory lock.
194+
///
195+
/// This guard holds a database connection from the connection pool. When the guard is dropped
196+
/// (goes out of scope), the connection is automatically returned to the pool. PostgreSQL
197+
/// automatically releases all advisory locks held by a connection when that connection is
198+
/// closed or returned to the pool.
199+
///
200+
/// Lock Release Mechanism:
201+
/// 1. Guard is created in acquire_exclusive() with the connection acquired from the pool
202+
/// 2. PostgreSQL advisory lock is held as long as the connection is active
203+
/// 3. When guard is dropped, the connection goes out of scope and is returned to the pool
204+
/// 4. PostgreSQL automatically releases the lock when the connection returns to the pool
205+
///
206+
/// This design is superior to explicit unlock calls because:
207+
/// - No risk of forgetting to unlock (guaranteed by Rust's Drop trait)
208+
/// - No fire-and-forget async tasks that might fail to execute
209+
/// - Lock release is atomic with connection return
210+
/// - Works correctly even if the runtime is shutting down
163211
struct PostgresExclusiveLockGuard {
164212
#[allow(dead_code)]
165213
key: String,

0 commit comments

Comments
 (0)