use std::future::Future;
use crate::error::PgError;
pub async fn with_retry<T, F, Fut>(
conn: &mut crate::Connection,
max_retries: u32,
f: F,
) -> Result<T, PgError>
where
F: Fn(&mut crate::Connection) -> Fut,
Fut: Future<Output = Result<T, PgError>>,
{
let mut attempt = 0;
loop {
match f(conn).await {
Ok(val) => return Ok(val),
Err(PgError::Server(ref e))
if e.is_serialization_failure() && attempt < max_retries =>
{
attempt += 1;
continue;
}
Err(PgError::Server(ref e)) if e.is_deadlock_detected() && attempt < max_retries => {
attempt += 1;
continue;
}
Err(e) => return Err(e),
}
}
}
pub async fn with_retry_any<T, F, Fut>(
conn: &mut crate::Connection,
max_retries: u32,
f: F,
) -> Result<T, PgError>
where
F: Fn(&mut crate::Connection) -> Fut,
Fut: Future<Output = Result<T, PgError>>,
{
let mut attempt = 0;
loop {
match f(conn).await {
Ok(val) => return Ok(val),
Err(e) if e.is_retryable() && attempt < max_retries => {
attempt += 1;
continue;
}
Err(e) => return Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::{PgServerError, TransportError};
#[test]
fn test_retry_on_serialization_failure() {
let err = PgError::Server(Box::new(PgServerError::from_fields(vec![
(b'S', "ERROR".to_string()),
(b'C', "40001".to_string()),
(b'M', "could not serialize access".to_string()),
])));
assert!(err.is_retryable());
}
#[test]
fn test_retry_on_deadlock() {
let err = PgError::Server(Box::new(PgServerError::from_fields(vec![
(b'S', "ERROR".to_string()),
(b'C', "40P01".to_string()),
(b'M', "deadlock detected".to_string()),
])));
assert!(err.is_retryable());
}
#[test]
fn test_no_retry_on_unique_violation() {
let err = PgError::Server(Box::new(PgServerError::from_fields(vec![
(b'S', "ERROR".to_string()),
(b'C', "23505".to_string()),
(b'M', "duplicate key".to_string()),
])));
assert!(!err.is_retryable());
}
#[test]
fn test_retry_on_timeout() {
let err = PgError::Transport(TransportError::Timeout);
assert!(err.is_retryable());
}
}