11//! This file contains LexeApiClient, the concrete impl of the ApiClient trait.
22
3+ use std:: cmp:: min;
34use std:: fmt:: { self , Display } ;
45use std:: time:: Duration ;
56
@@ -16,20 +17,20 @@ use reqwest::Client;
1617use serde:: de:: DeserializeOwned ;
1718use serde:: Serialize ;
1819use tokio:: time;
19- use tracing:: { debug, trace} ;
20+ use tracing:: { debug, trace, warn } ;
2021
2122use self :: ApiVersion :: * ;
2223use self :: BaseUrl :: * ;
2324use crate :: api:: * ;
2425
2526const 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
3127const 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
3435const GET : Method = Method :: GET ;
3536const 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