|  | 
| 1 | 1 | use std::fmt::{self, Debug}; | 
| 2 |  | -use std::io; | 
| 3 | 2 | use std::str::from_utf8; | 
| 4 | 3 | 
 | 
| 5 | 4 | use futures_channel::mpsc; | 
| @@ -197,6 +196,30 @@ impl PgListener { | 
| 197 | 196 |         Ok(self.connection.as_mut().unwrap()) | 
| 198 | 197 |     } | 
| 199 | 198 | 
 | 
|  | 199 | +    // same as `connection` but if fails to connect retries 5 times | 
|  | 200 | +    #[inline] | 
|  | 201 | +    async fn connection_with_recovery(&mut self) -> Result<&mut PgConnection, Error> { | 
|  | 202 | +        // retry max 5 times with these backoff durations | 
|  | 203 | +        let backoff_times = [0, 100, 1000, 2000, 10_000]; // ms | 
|  | 204 | + | 
|  | 205 | +        let mut last_err = None; | 
|  | 206 | +        for backoff_ms in backoff_times { | 
|  | 207 | +            match self.connect_if_needed().await { | 
|  | 208 | +                Ok(()) => return Ok(self.connection.as_mut().unwrap()), | 
|  | 209 | +                Err(err @ Error::Io(_)) => { | 
|  | 210 | +                    last_err = Some(err); | 
|  | 211 | + | 
|  | 212 | +                    crate::rt::sleep(std::time::Duration::from_millis(backoff_ms)).await; | 
|  | 213 | +                    continue; | 
|  | 214 | +                }, | 
|  | 215 | +                Err(other) => return Err(other), | 
|  | 216 | +            } | 
|  | 217 | +        } | 
|  | 218 | +         | 
|  | 219 | +        // if 5 retries later still got IO error, return the last one and stop | 
|  | 220 | +        Err(last_err.unwrap()) | 
|  | 221 | +    } | 
|  | 222 | + | 
| 200 | 223 |     /// Receives the next notification available from any of the subscribed channels. | 
| 201 | 224 |     /// | 
| 202 | 225 |     /// If the connection to PostgreSQL is lost, it is automatically reconnected on the next | 
| @@ -258,62 +281,61 @@ impl PgListener { | 
| 258 | 281 |     /// | 
| 259 | 282 |     /// [`eager_reconnect`]: PgListener::eager_reconnect | 
| 260 | 283 |     pub async fn try_recv(&mut self) -> Result<Option<PgNotification>, Error> { | 
|  | 284 | +        match self.recv_without_recovery().await { | 
|  | 285 | +            Ok(notification) => return Ok(Some(notification)), | 
|  | 286 | + | 
|  | 287 | +            // The connection is dead, ensure that it is dropped, | 
|  | 288 | +            // update self state, and loop to try again. | 
|  | 289 | +            Err(Error::Io(_)) => { | 
|  | 290 | +                if let Some(mut conn) = self.connection.take() { | 
|  | 291 | +                    self.buffer_tx = conn.inner.stream.notifications.take(); | 
|  | 292 | +                    // Close the connection in a background task, so we can continue. | 
|  | 293 | +                    conn.close_on_drop(); | 
|  | 294 | +                } | 
|  | 295 | + | 
|  | 296 | +                if self.eager_reconnect { | 
|  | 297 | +                    self.connection_with_recovery().await?; | 
|  | 298 | +                } | 
|  | 299 | + | 
|  | 300 | +                // lost connection | 
|  | 301 | +                return Ok(None); | 
|  | 302 | +            }, | 
|  | 303 | +            Err(e) => return Err(e), | 
|  | 304 | +        } | 
|  | 305 | +    } | 
|  | 306 | + | 
|  | 307 | +    async fn recv_without_recovery(&mut self) -> Result<PgNotification, Error> { | 
| 261 | 308 |         // Flush the buffer first, if anything | 
| 262 | 309 |         // This would only fill up if this listener is used as a connection | 
| 263 | 310 |         if let Some(notification) = self.next_buffered() { | 
| 264 |  | -            return Ok(Some(notification)); | 
|  | 311 | +            return Ok(notification); | 
| 265 | 312 |         } | 
| 266 | 313 | 
 | 
| 267 | 314 |         // Fetch our `CloseEvent` listener, if applicable. | 
| 268 | 315 |         let mut close_event = (!self.ignore_close_event).then(|| self.pool.close_event()); | 
| 269 | 316 | 
 | 
| 270 | 317 |         loop { | 
| 271 |  | -            let next_message = dbg!(self.connection().await)?.inner.stream.recv_unchecked(); | 
|  | 318 | +            let next_message = self.connection().await?.inner.stream.recv_unchecked(); | 
| 272 | 319 | 
 | 
| 273 | 320 |             let res = if let Some(ref mut close_event) = close_event { | 
| 274 | 321 |                 // cancels the wait and returns `Err(PoolClosed)` if the pool is closed | 
| 275 | 322 |                 // before `next_message` returns, or if the pool was already closed | 
| 276 |  | -                dbg!(close_event.do_until(next_message).await)? | 
|  | 323 | +                close_event.do_until(next_message).await? | 
| 277 | 324 |             } else { | 
| 278 | 325 |                 next_message.await | 
| 279 | 326 |             }; | 
| 280 | 327 | 
 | 
| 281 |  | -            let message = match res { | 
| 282 |  | -                Ok(message) => message, | 
| 283 |  | - | 
| 284 |  | -                // The connection is dead, ensure that it is dropped, | 
| 285 |  | -                // update self state, and loop to try again. | 
| 286 |  | -                Err(Error::Io(err)) => | 
| 287 |  | -                { | 
| 288 |  | -                    if let Some(mut conn) = self.connection.take() { | 
| 289 |  | -                        self.buffer_tx = conn.inner.stream.notifications.take(); | 
| 290 |  | -                        // Close the connection in a background task, so we can continue. | 
| 291 |  | -                        conn.close_on_drop(); | 
| 292 |  | -                    } | 
| 293 |  | - | 
| 294 |  | -                    if self.eager_reconnect { | 
| 295 |  | -                        dbg!(self.connect_if_needed().await)?; | 
| 296 |  | -                    } | 
| 297 |  | - | 
| 298 |  | -                    // lost connection | 
| 299 |  | -                    return Ok(None); | 
| 300 |  | -                } | 
| 301 |  | - | 
| 302 |  | -                // Forward other errors | 
| 303 |  | -                Err(error) => { | 
| 304 |  | -                    return Err(error); | 
| 305 |  | -                } | 
| 306 |  | -            }; | 
|  | 328 | +            let message = res?; | 
| 307 | 329 | 
 | 
| 308 | 330 |             match message.format { | 
| 309 | 331 |                 // We've received an async notification, return it. | 
| 310 | 332 |                 BackendMessageFormat::NotificationResponse => { | 
| 311 |  | -                    return Ok(Some(PgNotification(message.decode()?))); | 
|  | 333 | +                    return Ok(PgNotification(message.decode()?)); | 
| 312 | 334 |                 } | 
| 313 | 335 | 
 | 
| 314 | 336 |                 // Mark the connection as ready for another query | 
| 315 | 337 |                 BackendMessageFormat::ReadyForQuery => { | 
| 316 |  | -                    dbg!(self.connection().await)?.inner.pending_ready_for_query_count -= 1; | 
|  | 338 | +                    self.connection().await?.inner.pending_ready_for_query_count -= 1; | 
| 317 | 339 |                 } | 
| 318 | 340 | 
 | 
| 319 | 341 |                 // Ignore unexpected messages | 
|  | 
0 commit comments