Skip to main content

Mountain/RunTime/Execute/
RunWithRetry.rs

1#![allow(non_snake_case)]
2
3//! Retry a failing effect with exponential back-off, doubling the inter-
4//! attempt delay after each failure to avoid overwhelming the recovering
5//! system.
6
7use std::sync::Arc;
8
9use CommonLibrary::{
10	Effect::{ActionEffect::ActionEffect, ApplicationRunTime::ApplicationRunTime as ApplicationRunTimeTrait},
11	Environment::Requires::Requires,
12	Error::CommonError::CommonError,
13};
14
15use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
16
17impl ApplicationRunTime {
18	pub async fn RunWithRetry<TCapabilityProvider, TError, TOutput>(
19		&self,
20
21		Effect:ActionEffect<Arc<TCapabilityProvider>, TError, TOutput>,
22
23		MaximumRetries:u32,
24
25		InitialDelay:std::time::Duration,
26	) -> Result<TOutput, TError>
27	where
28		TCapabilityProvider: ?Sized + Send + Sync + 'static,
29		<Self as CommonLibrary::Environment::HasEnvironment::HasEnvironment>::EnvironmentType:
30			Requires<TCapabilityProvider>,
31		TError: From<CommonError> + Send + Sync + 'static + std::fmt::Display,
32		TOutput: Send + Sync + 'static, {
33		let mut RetryCount = 0;
34
35		let mut CurrentDelay = InitialDelay;
36
37		while RetryCount <= MaximumRetries {
38			match ApplicationRunTimeTrait::Run(self, Effect.clone()).await {
39				Ok(Result) => return Ok(Result),
40
41				Err(Error) => {
42					if RetryCount == MaximumRetries {
43						return Err(Error);
44					}
45
46					RetryCount += 1;
47
48					dev_log!(
49						"lifecycle",
50						"warn: [ApplicationRunTime] Effect execution failed (attempt {}): {}. Retrying in {:?}...",
51						RetryCount,
52						Error,
53						CurrentDelay
54					);
55
56					tokio::time::sleep(CurrentDelay).await;
57
58					CurrentDelay *= 2;
59				},
60			}
61		}
62
63		Err(
64			CommonError::Unknown { Description:format!("Effect execution failed after {} retries", MaximumRetries) }
65				.into(),
66		)
67	}
68}