RetryExecutor

Struct RetryExecutor 

Source
pub struct RetryExecutor<T, C: RetryConfig = DefaultRetryConfig> { /* private fields */ }
Expand description

Retry executor

Responsible for executing operations with retry strategies. Automatically executes retry logic according to configured retry strategies, delay strategies, failure/abort conditions, and triggers event listeners at appropriate times.

§Generic Parameters

  • T - The return value type of the operation
  • C - Retry configuration type, must implement RetryConfig trait, defaults to DefaultRetryConfig

§Core Features

  • Synchronous Retry: run() method executes synchronous operations, using post-check mechanism for timeout detection
  • Asynchronous Retry: run_async() method executes asynchronous operations, using tokio::time::timeout for real timeout interruption
  • Timeout Control: Supports single operation timeout (operation_timeout) and overall timeout (max_duration)
  • Event Listening: Supports event callbacks for retry, success, failure, abort, etc.
  • Flexible Configuration: Supports multiple delay strategies, error type identification, result value judgment, etc.

§Timeout Control

The executor supports two levels of timeout control:

  1. Single Operation Timeout (operation_timeout):

    • Controls the maximum execution time for each operation
    • Synchronous version (run): Checks if timeout occurred after operation completes (post-check mechanism)
    • Asynchronous version (run_async): Uses tokio::time::timeout to truly interrupt timeout operations
  2. Overall Timeout (max_duration):

    • Controls the maximum total time for the entire retry process (including all retries and delays)
    • Applies to both synchronous and asynchronous versions

§Usage Examples

§Synchronous Retry (Post-Check Timeout)

use prism3_retry::{RetryBuilder, RetryResult};
use std::time::Duration;

let executor = RetryBuilder::<String>::new()
    .set_max_attempts(3)
    .set_operation_timeout(Some(Duration::from_secs(5)))
    .build();

// Use RetryResult type alias to simplify function signature
let result: RetryResult<String> = executor.run(|| {
    // Can directly return any error type that implements Into<RetryError>
    // For example, using ? operator to handle io::Error will automatically
    // convert to RetryError
    std::thread::sleep(Duration::from_millis(100));
    Ok("SUCCESS".to_string())
});

§Asynchronous Retry (Real Timeout Interruption)

use prism3_retry::{RetryBuilder, RetryResult};
use std::time::Duration;

let executor = RetryBuilder::<String>::new()
    .set_max_attempts(3)
    .set_operation_timeout(Some(Duration::from_secs(5)))
    .build();

// Use RetryResult to make async function signature clearer
let result: RetryResult<String> = executor.run_async(|| async {
    // Asynchronous operation, truly interrupted on timeout
    tokio::time::sleep(Duration::from_millis(100)).await;
    Ok("SUCCESS".to_string())
}).await;

§Author

Haixing Hu

Implementations§

Source§

impl<T, C> RetryExecutor<T, C>
where T: Clone + PartialEq + Eq + Hash + Send + Sync + 'static, C: RetryConfig,

Source

pub fn run<F>(&self, operation: F) -> RetryResult<T>
where F: FnMut() -> Result<T, Box<dyn Error + Send + Sync>>,

Execute synchronous operation (with post-check timeout mechanism)

Execute synchronous operation according to configured retry strategy, until success, maximum retry count reached, or abort condition met.

§Timeout Control

This method uses post-check mechanism for timeout control:

  • After operation completes, check if execution time exceeds operation_timeout
  • If timeout, convert result to RetryError::OperationTimeout error and trigger retry
  • Note: Cannot truly interrupt ongoing synchronous operation

If you need to truly interrupt timeout operations, please use run_async() method.

§Parameters
  • operation - Operation to execute, returns Result<T, Box<dyn Error + Send + Sync>>
§Returns

Returns operation result or error

§Example
use prism3_retry::{RetryBuilder, RetryDelayStrategy, RetryResult};
use std::time::Duration;

let executor = RetryBuilder::new()
    .set_max_attempts(3)
    .set_delay_strategy(RetryDelayStrategy::Fixed { delay: Duration::from_secs(1) })
    .set_operation_timeout(Some(Duration::from_secs(5))) // Single operation post-check timeout
    .build();

// Use RetryResult to simplify function signature, leveraging From trait
// for automatic error conversion
let result: RetryResult<String> = executor.run(|| -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
    // Can return any standard error type, will be automatically
    // converted to RetryError
    // Example: std::fs::File::open("file.txt")?;
    // io::Error will be automatically converted to RetryError through
    // From trait
    Ok("SUCCESS".to_string())
});

assert!(result.is_ok());
Source

pub async fn run_async<F, Fut>(&self, operation: F) -> RetryResult<T>
where F: FnMut() -> Fut, Fut: Future<Output = Result<T, Box<dyn Error + Send + Sync>>>,

Execute asynchronous operation (with real timeout interruption)

Execute asynchronous operation according to configured retry strategy, with single operation timeout control.

§Timeout Control

This method uses tokio::time::timeout for real timeout interruption:

  • When operation execution time exceeds operation_timeout, the operation will be truly interrupted (cancelled)
  • After interruption, retry will be triggered (if there are remaining retry attempts)
  • Compared to the run() method’s post-check mechanism, this approach is more efficient and precise
§Difference from Synchronous Version
Featurerun() Sync Versionrun_async() Async Version
Timeout MechanismPost-check (check after operation completes)Real interruption (tokio::time::timeout)
Can Interrupt Operation❌ Cannot✅ Can
Timeout PrecisionDepends on operation completionPrecise to millisecond level
Applicable ScenarioShort synchronous operationsLong asynchronous operations
§Parameters
  • operation - Asynchronous operation to execute
§Returns

Returns operation result or error

§Example
use prism3_retry::{RetryBuilder, RetryDelayStrategy, RetryResult};
use std::time::Duration;

#[tokio::main]
async fn main() {
    let executor = RetryBuilder::<String>::new()
        .set_max_attempts(3)
        .set_operation_timeout(Some(Duration::from_secs(5)))  // Real timeout interruption
        .set_max_duration(Some(Duration::from_secs(30)))      // Overall timeout
        .set_delay_strategy(RetryDelayStrategy::Fixed {
            delay: Duration::from_secs(1)
        })
        .build();

    // Use RetryResult type alias to make code more concise
    let result: RetryResult<String> = executor.run_async(|| async {
        // Can also use ? operator in async operations, errors will be
        // automatically converted
        // Example: tokio::fs::read_to_string("file.txt").await?;
        tokio::time::sleep(Duration::from_millis(100)).await;
        Ok("SUCCESS".to_string())
    }).await;

    assert!(result.is_ok());
}
Source

pub fn config(&self) -> &RetryBuilder<T, C>

Get builder configuration

Auto Trait Implementations§

§

impl<T, C> Freeze for RetryExecutor<T, C>
where C: Freeze,

§

impl<T, C = DefaultRetryConfig> !RefUnwindSafe for RetryExecutor<T, C>

§

impl<T, C = DefaultRetryConfig> !Send for RetryExecutor<T, C>

§

impl<T, C = DefaultRetryConfig> !Sync for RetryExecutor<T, C>

§

impl<T, C> Unpin for RetryExecutor<T, C>
where C: Unpin, T: Unpin,

§

impl<T, C = DefaultRetryConfig> !UnwindSafe for RetryExecutor<T, C>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V