pub fn retryable_conflict_code(err: &(dyn std::error::Error + 'static)) -> Option<&'static str> {
let mut source = Some(err);
while let Some(err) = source {
if let Some(db) = err
.downcast_ref::<sqlx::Error>()
.and_then(|err| err.as_database_error())
{
match db.code().as_deref() {
Some("40P01") => return Some("40P01"),
Some("40001") => return Some("40001"),
_ => {}
}
}
source = err.source();
}
None
}
pub fn is_retryable_conflict(err: &(dyn std::error::Error + 'static)) -> bool {
retryable_conflict_code(err).is_some()
}
#[derive(Debug, Clone, Copy)]
pub struct TransientPolicy<P> {
pub is_transient: P,
pub max_retries: usize,
}
impl<P> TransientPolicy<P> {
pub fn new(is_transient: P) -> Self {
Self {
is_transient,
max_retries: super::DEFAULT_MAX_TRANSIENT_RETRIES,
}
}
#[must_use]
pub fn with_max_retries(self, max_retries: usize) -> Self {
Self {
max_retries,
..self
}
}
}
pub(super) fn sqlstate_is_transient<E: std::error::Error + 'static>(error: &E) -> bool {
is_retryable_conflict(error)
}