use spate_core::error::ErrorClass;
pub(crate) fn classify(e: &object_store::Error) -> ErrorClass {
use object_store::Error as E;
match e {
E::NotFound { .. }
| E::Precondition { .. }
| E::NotModified { .. }
| E::AlreadyExists { .. }
| E::InvalidPath { .. }
| E::NotSupported { .. }
| E::NotImplemented { .. }
| E::PermissionDenied { .. }
| E::Unauthenticated { .. }
| E::UnknownConfigurationKey { .. } => ErrorClass::Fatal,
_ => ErrorClass::Retryable,
}
}
pub(crate) fn is_pipeline_fatal(e: &object_store::Error) -> bool {
use object_store::Error as E;
matches!(
e,
E::InvalidPath { .. }
| E::NotSupported { .. }
| E::NotImplemented { .. }
| E::PermissionDenied { .. }
| E::Unauthenticated { .. }
| E::UnknownConfigurationKey { .. }
)
}
pub(crate) fn poison_kind(e: &object_store::Error) -> crate::split_ctx::PoisonKind {
use crate::split_ctx::PoisonKind;
use object_store::Error as E;
match e {
E::NotFound { .. } => PoisonKind::NotFound,
E::Precondition { .. } | E::NotModified { .. } | E::AlreadyExists { .. } => {
PoisonKind::EtagDrift
}
_ => PoisonKind::Undecodable,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn object_level_failures_classify_as_poison_not_pipeline_fatal() {
use object_store::Error as E;
let src = || "boom".into();
let table: Vec<(E, bool, bool)> = vec![
(
E::NotFound {
path: "k".into(),
source: src(),
},
true,
false,
),
(
E::Precondition {
path: "k".into(),
source: src(),
},
true,
false,
),
(
E::PermissionDenied {
path: "k".into(),
source: src(),
},
true,
true,
),
(
E::Unauthenticated {
path: "k".into(),
source: src(),
},
true,
true,
),
(
E::UnknownConfigurationKey {
store: "s3",
key: "nope".into(),
},
true,
true,
),
(
E::Generic {
store: "s3",
source: src(),
},
false,
false,
),
];
for (e, non_retryable, fatal) in &table {
assert_eq!(
classify(e) != crate::error::ErrorClass::Retryable,
*non_retryable,
"classify({e})"
);
assert_eq!(is_pipeline_fatal(e), *fatal, "is_pipeline_fatal({e})");
}
}
}