@@ -98,6 +98,48 @@ use tracing::{debug, error, info, warn, Instrument as _};
9898
9999struct Job ( ClientOptions ) ;
100100
101+ /// Check if an error is retryable (HTTP 502, 503, 504, timeouts, connection errors)
102+ fn is_retryable_error ( err : & anyhow:: Error ) -> bool {
103+ // Check for reqwest errors in the error chain
104+ if let Some ( reqwest_err) = err. downcast_ref :: < reqwest:: Error > ( ) {
105+ // Check for retryable HTTP status codes (502, 503, 504)
106+ if let Some ( status) = reqwest_err. status ( ) {
107+ return status == reqwest:: StatusCode :: BAD_GATEWAY
108+ || status == reqwest:: StatusCode :: SERVICE_UNAVAILABLE
109+ || status == reqwest:: StatusCode :: GATEWAY_TIMEOUT ;
110+ }
111+ // Check for connection errors or timeouts
112+ return reqwest_err. is_timeout ( ) || reqwest_err. is_connect ( ) ;
113+ }
114+ false
115+ }
116+
117+ /// Retry a faucet operation with exponential backoff
118+ async fn retry_faucet_operation < F , Fut , T > ( operation : F ) -> anyhow:: Result < T >
119+ where
120+ F : Fn ( ) -> Fut ,
121+ Fut : std:: future:: Future < Output = anyhow:: Result < T > > ,
122+ {
123+ let max_retries = 5 ;
124+ let mut attempt = 0 ;
125+
126+ loop {
127+ attempt += 1 ;
128+ match operation ( ) . await {
129+ Ok ( result) => return Ok ( result) ,
130+ Err ( err) if attempt < max_retries && is_retryable_error ( & err) => {
131+ let backoff_ms = 100 * 2_u64 . pow ( attempt - 1 ) ;
132+ warn ! (
133+ "Faucet operation failed with retryable error (attempt {}/{}): {:?}. Retrying after {}ms" ,
134+ attempt, max_retries, err, backoff_ms
135+ ) ;
136+ tokio:: time:: sleep ( Duration :: from_millis ( backoff_ms) ) . await ;
137+ }
138+ Err ( err) => return Err ( err) ,
139+ }
140+ }
141+ }
142+
101143fn read_json ( string : Option < String > , path : Option < PathBuf > ) -> anyhow:: Result < Vec < u8 > > {
102144 let value = match ( string, path) {
103145 ( Some ( _) , Some ( _) ) => bail ! ( "cannot have both a json string and file" ) ,
@@ -1108,8 +1150,12 @@ impl Runnable for Job {
11081150 let client = client. clone ( ) ;
11091151 let faucet_client = faucet_client. clone ( ) ;
11101152 join_set. spawn ( async move {
1111- client. wallet_init ( Some ( & faucet_client) ) . await ?;
1112- client. request_chain ( & faucet_client, true ) . await ?;
1153+ retry_faucet_operation ( || client. wallet_init ( Some ( & faucet_client) ) )
1154+ . await ?;
1155+ retry_faucet_operation ( || {
1156+ client. request_chain ( & faucet_client, true )
1157+ } )
1158+ . await ?;
11131159 Ok :: < _ , anyhow:: Error > ( ( ) )
11141160 } ) ;
11151161 }
@@ -1172,8 +1218,12 @@ impl Runnable for Job {
11721218 for client in clients. clone ( ) {
11731219 let faucet_client = faucet_client. clone ( ) ;
11741220 join_set. spawn ( async move {
1175- client. wallet_init ( Some ( & faucet_client) ) . await ?;
1176- client. request_chain ( & faucet_client, true ) . await ?;
1221+ retry_faucet_operation ( || client. wallet_init ( Some ( & faucet_client) ) )
1222+ . await ?;
1223+ retry_faucet_operation ( || {
1224+ client. request_chain ( & faucet_client, true )
1225+ } )
1226+ . await ?;
11771227 Ok :: < _ , anyhow:: Error > ( ( ) )
11781228 } ) ;
11791229 }
0 commit comments