@@ -374,6 +374,36 @@ impl Batcher {
374374 updated_user_states
375375 }
376376
377+ /// Helper to apply 15-second timeout to user lock acquisition with consistent logging and metrics
378+ async fn try_user_lock_with_timeout < F , T > ( & self , addr : Address , lock_future : F ) -> Option < T >
379+ where
380+ F : std:: future:: Future < Output = T > ,
381+ {
382+ match timeout ( Duration :: from_secs ( 15 ) , lock_future) . await {
383+ Ok ( result) => Some ( result) ,
384+ Err ( _) => {
385+ warn ! ( "User lock acquisition timed out for address {}" , addr) ;
386+ self . metrics . inc_message_handler_user_lock_timeout ( ) ;
387+ None
388+ }
389+ }
390+ }
391+
392+ /// Helper to apply 15-second timeout to batch lock acquisition with consistent logging and metrics
393+ async fn try_batch_lock_with_timeout < F , T > ( & self , lock_future : F ) -> Option < T >
394+ where
395+ F : std:: future:: Future < Output = T > ,
396+ {
397+ match timeout ( Duration :: from_secs ( 15 ) , lock_future) . await {
398+ Ok ( result) => Some ( result) ,
399+ Err ( _) => {
400+ warn ! ( "Batch lock acquisition timed out" ) ;
401+ self . metrics . inc_message_handler_batch_lock_timeout ( ) ;
402+ None
403+ }
404+ }
405+ }
406+
377407 pub async fn listen_connections ( self : Arc < Self > , address : & str ) -> Result < ( ) , BatcherError > {
378408 // Create the event loop and TCP listener we'll accept connections on.
379409 let listener = TcpListener :: bind ( address)
@@ -659,7 +689,14 @@ impl Batcher {
659689 let user_state_ref = self . user_states . get ( & address) ;
660690 match user_state_ref {
661691 Some ( user_state_ref) => {
662- let user_state_guard = user_state_ref. lock ( ) . await ;
692+ let Some ( user_state_guard) = self
693+ . try_user_lock_with_timeout ( address, user_state_ref. lock ( ) )
694+ . await
695+ else {
696+ send_message ( ws_conn_sink. clone ( ) , GetNonceResponseMessage :: ServerBusy )
697+ . await ;
698+ return Ok ( ( ) ) ;
699+ } ;
663700 Some ( user_state_guard. nonce )
664701 }
665702 None => None ,
@@ -795,7 +832,13 @@ impl Batcher {
795832 } ;
796833
797834 // We acquire the lock on the user state, now everything will be processed sequentially
798- let mut user_state_guard = user_state_ref. lock ( ) . await ;
835+ let Some ( mut user_state_guard) = self
836+ . try_user_lock_with_timeout ( addr, user_state_ref. lock ( ) )
837+ . await
838+ else {
839+ send_message ( ws_conn_sink. clone ( ) , SubmitProofResponseMessage :: ServerBusy ) . await ;
840+ return Ok ( ( ) ) ;
841+ } ;
799842
800843 // If the user state was not present, we need to get the nonce from the Ethereum contract and update the dummy user state
801844 if !is_user_in_state {
@@ -907,7 +950,13 @@ impl Batcher {
907950 // * Perform validation over batcher queue *
908951 // * ---------------------------------------------------------------------*
909952
910- let mut batch_state_lock = self . batch_state . lock ( ) . await ;
953+ let Some ( mut batch_state_lock) = self
954+ . try_batch_lock_with_timeout ( self . batch_state . lock ( ) )
955+ . await
956+ else {
957+ send_message ( ws_conn_sink. clone ( ) , SubmitProofResponseMessage :: ServerBusy ) . await ;
958+ return Ok ( ( ) ) ;
959+ } ;
911960 if batch_state_lock. is_queue_full ( ) {
912961 debug ! ( "Batch queue is full. Evaluating if the incoming proof can replace a lower-priority entry." ) ;
913962
@@ -1070,7 +1119,14 @@ impl Batcher {
10701119 ) {
10711120 let replacement_max_fee = nonced_verification_data. max_fee ;
10721121 let nonce = nonced_verification_data. nonce ;
1073- let mut batch_state_guard = self . batch_state . lock ( ) . await ; // Second: batch lock
1122+ let Some ( mut batch_state_guard) = self
1123+ . try_batch_lock_with_timeout ( self . batch_state . lock ( ) )
1124+ . await
1125+ else {
1126+ drop ( user_state_guard) ;
1127+ send_message ( ws_conn_sink. clone ( ) , SubmitProofResponseMessage :: ServerBusy ) . await ;
1128+ return ;
1129+ } ;
10741130 let Some ( entry) = batch_state_guard. get_entry ( addr, nonce) else {
10751131 drop ( batch_state_guard) ;
10761132 drop ( user_state_guard) ;
0 commit comments