Skip to main content
Glama
retry_classifier.rs4.59 kB
//! Retry classifier for S3 NoSuchKey errors to handle eventual consistency. //! //! This classifier integrates with AWS SDK's retry mechanism to automatically //! retry GetObject operations that return NoSuchKey errors. This is necessary //! because S3 has eventual consistency - an object that was just written may //! not be immediately visible in subsequent read operations. //! //! The classifier runs with higher priority than default classifiers, allowing //! it to override their `NoActionIndicated` decision for NoSuchKey errors //! specifically, without affecting retry behavior for other error types. use aws_sdk_s3::operation::get_object::GetObjectError; use aws_smithy_runtime_api::client::{ interceptors::context::InterceptorContext, retries::classifiers::{ ClassifyRetry, RetryAction, RetryClassifierPriority, }, }; /// Retry classifier that marks S3 NoSuchKey errors as retryable. /// /// This classifier detects `GetObjectError::NoSuchKey` errors and marks them /// as transient errors, triggering the SDK's retry mechanism. This handles /// the eventual consistency case where an object was just written but is not /// yet visible. /// /// The classifier uses type-safe matching on GetObjectError::NoSuchKey. /// /// # Priority /// /// This classifier runs with higher priority (AFTER default classifiers), /// allowing it to override their `NoActionIndicated` decision for NoSuchKey /// specifically. It returns `NoActionIndicated` for all other errors, /// deferring to defaults. #[derive(Debug, Clone, Default)] pub struct NoSuchKeyRetryClassifier; impl ClassifyRetry for NoSuchKeyRetryClassifier { fn classify_retry(&self, ctx: &InterceptorContext) -> RetryAction { // Check for a result let output_or_error = ctx.output_or_error(); // Check for an error let error = match output_or_error { Some(Ok(_)) | None => return RetryAction::NoActionIndicated, Some(Err(err)) => err, }; if let Some(operation_error) = error.as_operation_error() { if let Some(get_object_error) = operation_error.downcast_ref::<GetObjectError>() { if matches!(get_object_error, GetObjectError::NoSuchKey(_)) { return RetryAction::transient_error(); } } } // Not a NoSuchKey error - let other classifiers handle it RetryAction::NoActionIndicated } fn name(&self) -> &'static str { "NoSuchKeyRetryClassifier" } fn priority(&self) -> RetryClassifierPriority { // Run AFTER default classifiers so we can override their NoActionIndicated // decision for NoSuchKey errors specifically RetryClassifierPriority::run_after( RetryClassifierPriority::modeled_as_retryable_classifier(), ) } } #[cfg(test)] mod tests { use aws_smithy_runtime_api::client::{ interceptors::context::{ Error, Input, }, orchestrator::OrchestratorError, }; use super::*; // Helper to create an InterceptorContext with a specific error fn create_context_with_error(error: GetObjectError) -> InterceptorContext { let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(error)))); ctx } #[test] fn test_nosuchkey_is_retryable() { let classifier = NoSuchKeyRetryClassifier; let error = GetObjectError::NoSuchKey( aws_sdk_s3::types::error::NoSuchKey::builder() .message("The specified key does not exist.") .build(), ); let ctx = create_context_with_error(error); let result = classifier.classify_retry(&ctx); assert!( matches!(result, RetryAction::RetryIndicated(_)), "NoSuchKey should be retryable, got: {result:?}", ); } #[test] fn test_other_errors_not_retryable() { let classifier = NoSuchKeyRetryClassifier; let error = GetObjectError::InvalidObjectState( aws_sdk_s3::types::error::InvalidObjectState::builder() .message("Object is archived") .build(), ); let ctx = create_context_with_error(error); let result = classifier.classify_retry(&ctx); assert!( matches!(result, RetryAction::NoActionIndicated), "InvalidObjectState should return NoActionIndicated, got: {result:?}", ); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server