@@ -202,12 +202,19 @@ def modern_read_with_retry(session, server_selector, context, &block)
202202 session ,
203203 timeout : context &.remaining_timeout_sec
204204 )
205- yield server
205+ result = yield server
206+ retry_policy . record_success ( is_retry : false )
207+ result
206208 rescue *retryable_exceptions , Error ::OperationFailure ::Family , Auth ::Unauthorized , Error ::PoolError => e
207209 e . add_notes ( 'modern retry' , 'attempt 1' )
208210 raise e if session . in_transaction?
209- raise e if !is_retryable_exception? ( e ) && !e . write_retryable?
210- retry_read ( e , session , server_selector , context : context , failed_server : server , &block )
211+
212+ if retryable_overload_error? ( e )
213+ overload_read_retry ( e , session , server_selector , context , server , error_count : 1 , &block )
214+ else
215+ raise e if !is_retryable_exception? ( e ) && !e . write_retryable?
216+ retry_read ( e , session , server_selector , context : context , failed_server : server , &block )
217+ end
211218 end
212219
213220 # Attempts to do a "legacy" read with retry. The operation will be
@@ -289,11 +296,16 @@ def retry_read(original_error, session, server_selector, context: nil, failed_se
289296 begin
290297 context &.check_timeout!
291298 attempt = attempt ? attempt + 1 : 2
292- yield server , true
299+ result = yield server , true
300+ retry_policy . record_success ( is_retry : true )
301+ result
293302 rescue Error ::TimeoutError
294303 raise
295304 rescue *retryable_exceptions => e
296305 e . add_notes ( 'modern retry' , "attempt #{ attempt } " )
306+ if retryable_overload_error? ( e )
307+ return overload_read_retry ( e , session , server_selector , context , server , error_count : attempt , &block )
308+ end
297309 if context &.csot?
298310 failed_server = server
299311 retry
@@ -302,6 +314,10 @@ def retry_read(original_error, session, server_selector, context: nil, failed_se
302314 end
303315 rescue Error ::OperationFailure ::Family , Error ::PoolError => e
304316 e . add_note ( 'modern retry' )
317+ if retryable_overload_error? ( e )
318+ e . add_note ( "attempt #{ attempt } " )
319+ return overload_read_retry ( e , session , server_selector , context , server , error_count : attempt , &block )
320+ end
305321 if e . write_retryable?
306322 e . add_note ( "attempt #{ attempt } " )
307323 if context &.csot?
@@ -321,6 +337,51 @@ def retry_read(original_error, session, server_selector, context: nil, failed_se
321337 end
322338 end
323339
340+ # Retry loop for overload errors with exponential backoff.
341+ # Each retry sleeps with jittered backoff, respects MAX_RETRIES,
342+ # and consumes a token from the bucket when adaptive retries
343+ # are enabled.
344+ def overload_read_retry ( last_error , session , server_selector , context , failed_server , error_count :, &block )
345+ loop do
346+ delay = retry_policy . backoff_delay ( error_count )
347+ unless retry_policy . should_retry_overload? ( error_count , delay , context : context )
348+ raise last_error
349+ end
350+ log_retry ( last_error , message : 'Read retry (overload backoff)' )
351+ sleep ( delay )
352+
353+ begin
354+ server = select_server (
355+ cluster , server_selector , session , failed_server ,
356+ error : last_error ,
357+ timeout : context &.remaining_timeout_sec
358+ )
359+ rescue Error , Error ::AuthError => e
360+ last_error . add_note ( "later retry failed: #{ e . class } : #{ e } " )
361+ raise last_error
362+ end
363+
364+ begin
365+ context &.check_timeout!
366+ result = yield server , true
367+ retry_policy . record_success ( is_retry : true )
368+ return result
369+ rescue Error ::TimeoutError
370+ raise
371+ rescue *retryable_exceptions , Error ::OperationFailure ::Family , Auth ::Unauthorized , Error ::PoolError => e
372+ error_count += 1
373+ e . add_notes ( 'modern retry' , "attempt #{ error_count } " )
374+ is_overload = retryable_overload_error? ( e )
375+ unless is_overload || is_retryable_exception? ( e ) || e . write_retryable?
376+ raise e
377+ end
378+ retry_policy . record_non_overload_retry_failure unless is_overload
379+ failed_server = server
380+ last_error = e
381+ end
382+ end
383+ end
384+
324385 def select_server_for_retry ( original_error , session , server_selector , context , failed_server )
325386 select_server (
326387 cluster ,
0 commit comments