Skip to main content

Mountain/RunTime/Execute/
RunWithRetry.rs

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