Skip to content

Commit d048eab

Browse files
committed
node: Implement exponential backup
1 parent ce38a89 commit d048eab

File tree

1 file changed

+19
-17
lines changed

1 file changed

+19
-17
lines changed

node/src/api/client.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! This file contains LexeApiClient, the concrete impl of the ApiClient trait.
22
3+
use std::cmp::min;
34
use std::fmt::{self, Display};
45
use std::time::Duration;
56

@@ -16,20 +17,20 @@ use reqwest::Client;
1617
use serde::de::DeserializeOwned;
1718
use serde::Serialize;
1819
use tokio::time;
19-
use tracing::{debug, trace};
20+
use tracing::{debug, trace, warn};
2021

2122
use self::ApiVersion::*;
2223
use self::BaseUrl::*;
2324
use crate::api::*;
2425

2526
const API_REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
26-
/// How long to wait after the second failed API request before trying again.
27-
/// This is relatively long since if the second try failed, it probably means
28-
/// that the backend is down, which could be the case for a while.
29-
const RETRY_INTERVAL: Duration = Duration::from_secs(15);
30-
/// 0 retries by default
3127
const DEFAULT_RETRIES: usize = 0;
3228

29+
// Exponential backup
30+
const INITIAL_WAIT_MS: u64 = 250;
31+
const EXP_MULTIPLE: u64 = 250;
32+
const MAXIMUM_WAIT_MS: u64 = 32_000;
33+
3334
// Avoid `Method::` prefix. Associated constants can't be imported
3435
const GET: Method = Method::GET;
3536
const PUT: Method = Method::PUT;
@@ -223,26 +224,27 @@ impl LexeApiClient {
223224
data: &D,
224225
retries: usize,
225226
) -> Result<T, ApiError> {
226-
let mut retry_timer = time::interval(RETRY_INTERVAL);
227-
227+
// Serialize request parts
228228
let parts = self.serialize_parts(method, base, ver, endpoint, data)?;
229229

230+
// Exponential backup
231+
let mut backup_durations = (0..)
232+
.map(|index| INITIAL_WAIT_MS * EXP_MULTIPLE.pow(index))
233+
.map(|wait| min(wait, MAXIMUM_WAIT_MS))
234+
.map(Duration::from_millis);
235+
230236
// Do the 'retries' first and return early if successful.
231237
// This block is a noop if retries == 0.
232238
for _ in 0..retries {
233239
let res = self.send_request(&parts).await;
234240
if res.is_ok() {
235241
return res;
236242
} else {
237-
// TODO log errors here
238-
239-
// Since the first tick resolves immediately, and we tick only
240-
// on failures, the first failed attempt is immediately followed
241-
// up with second attempt (to encode that sometimes messages are
242-
// dropped during normal operation), but all following attempts
243-
// wait the full timeout (to encode that the node backend is
244-
// probably down so we want to wait a relatively long timeout).
245-
retry_timer.tick().await;
243+
let method = &parts.method;
244+
let url = &parts.url;
245+
warn!("{method} {url} failed.");
246+
247+
time::sleep(backup_durations.next().unwrap()).await;
246248
}
247249
}
248250

0 commit comments

Comments
 (0)