1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! PostgreSQL advisory locking for cross-replica reconciliation safety.
//!
//! Uses `pg_try_advisory_lock` / `pg_advisory_unlock` to prevent concurrent
//! inspect/diff/apply cycles against the same database, even when multiple
//! operator replicas are running.
//!
//! The lock key is computed **server-side**, on the very connection that will
//! take the lock:
//!
//! ```sql
//! SELECT hashtextextended('pgroles-reconcile:' || current_database(), 0)
//! ```
//!
//! Server-side keying is what makes the key canonical. A client-side key
//! derived from how the operator *names* a database (Secret coordinates,
//! hostnames, connection strings) would give the same physical database a
//! different key for every alias — two Secrets pointing at one database, a
//! CNAME versus the raw host, a pooler in front of the server — and each
//! alias would sail past the others' locks. `current_database()` is evaluated
//! by the server itself, so any two sessions connected to the same database
//! compute the same key regardless of the client-side path that got them
//! there, and they contend on the same lock.
//!
//! Session-level advisory locks are bound to the connection that acquired
//! them, so this module checks out a dedicated [`PoolConnection`] and holds it
//! for the lifetime of the lock. Key computation, acquire, and release all
//! execute on that same underlying database session.
use PoolConnection;
use ;
/// A held advisory lock that must be explicitly released.
///
/// Holds a dedicated [`PoolConnection`] so that the lock acquire and release
/// always run on the same PostgreSQL session (advisory locks are
/// session-scoped).
/// Last-resort safety net for a lock that was never released.
///
/// `release()` is async, so it cannot be called from `Drop`; the only ways to
/// get here are a cancelled reconcile future (controller shutdown, a timeout
/// wrapped around the locked phase) or a future refactor that adds an early
/// return between acquire and release. Simply dropping the [`PoolConnection`]
/// would be the dangerous outcome: sqlx hands that connection *back to the
/// pool* with the session-level lock still held, so the next reconcile to
/// check it out would re-enter the lock as its own while every other session
/// stays blocked out until `max_lifetime` recycles it. Detaching the
/// connection instead takes it out of the pool for good, and closing the
/// socket is what makes PostgreSQL drop the session's advisory locks.
/// Attempt to acquire a session-level advisory lock on the connected database.
///
/// Checks out a dedicated connection from the pool, computes the canonical
/// lock key **on that connection** via
/// `hashtextextended('pgroles-reconcile:' || current_database(), 0)`
/// (`hashtextextended` exists since PostgreSQL 11; the project requires 16+;
/// negative keys are fine for `pg_try_advisory_lock`), and then executes
/// `pg_try_advisory_lock` on the same connection. If the lock is acquired, the
/// connection is kept inside the returned [`AdvisoryLock`] so that both
/// acquire and release run on the same session.
///
/// `database_identity` is logging context only — the key derives entirely from
/// the server's own `current_database()`, so differently-named identities that
/// reach the same database contend on the same key.
///
/// Returns `Ok(Some(AdvisoryLock))` if the lock was acquired, `Ok(None)` if it
/// is already held by another session, or `Err` on query failure.
pub async