88#[ cfg( test) ]
99mod tests {
1010 use crate :: common:: { connect_with_tls, PG_PORT , PROXY } ;
11- use std:: sync:: Arc ;
1211 use std:: time:: Instant ;
13- use tokio:: sync:: Notify ;
1412 use tokio:: task:: JoinSet ;
1513 use tokio:: time:: { timeout, Duration } ;
14+ use tokio_postgres:: SimpleQueryMessage ;
1615
1716 /// Advisory lock ID used in isolation tests. Arbitrary value — just needs to be
1817 /// unique across concurrently running test suites against the same database.
@@ -120,9 +119,8 @@ mod tests {
120119
121120 /// An advisory-lock-blocked connection through the proxy does not block other proxy connections.
122121 ///
123- /// Note: Connection B notifies readiness before `pg_advisory_lock` reaches PostgreSQL.
124- /// The 500ms sleep provides a generous margin for the lock attempt to reach PG, but is
125- /// not strictly guaranteed. In practice this has not caused flakiness.
122+ /// Uses pg_locks polling to deterministically wait for client_b to be blocked on the
123+ /// advisory lock, rather than relying on a fixed sleep.
126124 #[ tokio:: test]
127125 async fn advisory_lock_blocked_connection_does_not_block_proxy ( ) {
128126 let lock_query = format ! ( "SELECT pg_advisory_lock({ADVISORY_LOCK_ID})" ) ;
@@ -136,15 +134,12 @@ mod tests {
136134 . await
137135 . unwrap ( ) ;
138136
139- let a_ready = Arc :: new ( Notify :: new ( ) ) ;
140- let a_ready_tx = a_ready. clone ( ) ;
141137 let b_lock_query = lock_query. clone ( ) ;
142138 let b_unlock_query = unlock_query. clone ( ) ;
143139
144140 // Connection B: through proxy, attempt to acquire the same lock (will block)
145141 let b_handle = tokio:: spawn ( async move {
146142 let client_b = connect_with_tls ( PROXY ) . await ;
147- a_ready_tx. notify_one ( ) ;
148143 // This will block until A releases the lock
149144 client_b
150145 . simple_query ( & b_lock_query)
@@ -157,9 +152,23 @@ mod tests {
157152 . unwrap ( ) ;
158153 } ) ;
159154
160- // Wait for B to be connected and attempting the lock
161- a_ready. notified ( ) . await ;
162- tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
155+ // Poll pg_locks until client_b is observed waiting for the advisory lock
156+ let poll_query = format ! (
157+ "SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND NOT granted AND classid = 0 AND objid = {ADVISORY_LOCK_ID}"
158+ ) ;
159+ let deadline = Instant :: now ( ) + Duration :: from_secs ( 10 ) ;
160+ loop {
161+ let result = client_a. simple_query ( & poll_query) . await . unwrap ( ) ;
162+ let has_waiting = result. iter ( ) . any ( |m| matches ! ( m, SimpleQueryMessage :: Row ( _) ) ) ;
163+ if has_waiting {
164+ break ;
165+ }
166+ assert ! (
167+ Instant :: now( ) < deadline,
168+ "Timed out waiting for client_b to be blocked on advisory lock"
169+ ) ;
170+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
171+ }
163172
164173 // Connection C: through proxy, should complete immediately despite B being blocked
165174 let start = Instant :: now ( ) ;
0 commit comments