Skip to content

Commit 79097fd

Browse files
authored
feat: silent flow retries on failure (#131)
* retries * bump version * apply clippy
1 parent 47d8926 commit 79097fd

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

core/src/analytics/event.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use serde::Serialize;
22
use std::fmt;
33
use std::fmt::Display;
44

5+
use crate::errors::AttemptError;
6+
57
#[allow(non_camel_case_types)]
68
#[derive(Clone, Serialize)]
79
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
@@ -65,6 +67,10 @@ pub enum Event {
6567
LAUNCHER_UPDATE_DOWNLOADED {
6668
version: String,
6769
},
70+
FLOW_ATTEMPT_ERROR {
71+
message: String,
72+
attempt: u8,
73+
}
6874
}
6975

7076
impl Display for Event {
@@ -93,7 +99,14 @@ impl Display for Event {
9399
Event::LAUNCHER_UPDATE_CANCELLED { .. } => "Launcher Update Cancelled",
94100
Event::LAUNCHER_UPDATE_ERROR { .. } => "Launcher Update Error",
95101
Event::LAUNCHER_UPDATE_DOWNLOADED { .. } => "Launcher Update Downloaded",
102+
Event::FLOW_ATTEMPT_ERROR { .. } => "Launcher Attempt Error",
96103
}
97104
)
98105
}
99106
}
107+
108+
impl From<&AttemptError> for Event {
109+
fn from(value: &AttemptError) -> Self {
110+
Self::FLOW_ATTEMPT_ERROR { message: value.error.to_string(), attempt: value.attempt }
111+
}
112+
}

core/src/errors.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ impl From<&FlowError> for Status {
2020
}
2121
}
2222

23+
#[derive(Error, Debug)]
24+
pub struct AttemptError {
25+
#[source]
26+
pub(crate) error: StepError,
27+
pub(crate) attempt: u8,
28+
}
29+
30+
impl Display for AttemptError {
31+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32+
write!(
33+
f,
34+
"Error on attempt: {}, cause: {}",
35+
self.attempt, self.error
36+
)
37+
}
38+
}
39+
2340
pub type StepResult = std::result::Result<(), StepError>;
2441

2542
pub type StepResultTyped<T> = std::result::Result<T, StepError>;

core/src/flow.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::channel::EventChannel;
22
use crate::deeplink_bridge::{
33
PlaceDeeplinkError, PlaceDeeplinkResult, place_deeplink_and_wait_until_consumed,
44
};
5-
use crate::errors::{StepError, StepResultTyped};
5+
use crate::errors::{AttemptError, StepError, StepResultTyped};
66
use crate::instances::RunningInstances;
77
use crate::protocols::Protocol;
88
use crate::{
@@ -73,6 +73,8 @@ pub struct LaunchFlow {
7373
download_step: DownloadStep,
7474
install_step: InstallStep,
7575
app_launch_step: AppLaunchStep,
76+
77+
analytics: Arc<Mutex<Analytics>>,
7678
}
7779

7880
impl LaunchFlow {
@@ -87,12 +89,15 @@ impl LaunchFlow {
8789
download_step: DownloadStep {
8890
analytics: analytics.clone(),
8991
},
90-
install_step: InstallStep { analytics },
92+
install_step: InstallStep {
93+
analytics: analytics.clone(),
94+
},
9195
app_launch_step: AppLaunchStep {
9296
installs_hub,
9397
running_instances,
9498
protocol,
9599
},
100+
analytics,
96101
}
97102
}
98103

@@ -101,17 +106,43 @@ impl LaunchFlow {
101106
channel: &T,
102107
state: Arc<Mutex<LaunchFlowState>>,
103108
) -> std::result::Result<(), FlowError> {
104-
let result = self.launch_internal(channel, state.clone()).await;
105-
if let Err(e) = result {
106-
log::error!("Error during the flow {} {:#?}", e, e);
107-
sentry::capture_error(&e);
109+
const SILENT_ATTEMPTS_COUNT: u8 = 3;
110+
111+
let mut last_error: Option<AttemptError> = None;
112+
113+
for attempt in 1..=SILENT_ATTEMPTS_COUNT {
114+
let result = self.launch_internal(channel, state.clone()).await;
115+
116+
if let Err(e) = result {
117+
log::error!(
118+
"Error during the flow. Attempt: {}, Cause {} {:#?}",
119+
attempt,
120+
e,
121+
e
122+
);
123+
let e = AttemptError {
124+
error: e,
125+
attempt,
126+
};
127+
128+
sentry::capture_error(&e);
129+
self.analytics.lock().await.track_and_flush_silent((&e).into()).await;
130+
131+
last_error = Some(e);
132+
continue;
133+
}
134+
135+
return std::result::Result::Ok(());
136+
}
137+
138+
if let Some(e) = last_error {
108139
let error = FlowError {
109-
user_message: e.user_message().to_owned(),
140+
user_message: e.error.user_message().to_owned(),
110141
};
111-
return std::result::Result::Err(error);
142+
std::result::Result::Err(error)
143+
} else {
144+
std::result::Result::Ok(())
112145
}
113-
114-
std::result::Result::Ok(())
115146
}
116147

117148
async fn launch_internal<T: EventChannel>(

0 commit comments

Comments
 (0)