@@ -425,6 +425,21 @@ def _warn_queue_before_connect(self) -> None:
425425QueuedRequest = namedtuple ("QueuedRequest" , ["endpoint" , "data" ])
426426Bucket = namedtuple ("Bucket" , ["id" , "type" ])
427427
428+ # Bounds for the delay before retrying a queued request after a
429+ # transient server error (e.g. 429/503), honoring Retry-After if given.
430+ RETRY_DELAY_DEFAULT = 0.5
431+ RETRY_DELAY_MAX = 60.0
432+
433+
434+ def _retry_delay (response : req .Response ) -> float :
435+ """Delay before retrying, honoring the Retry-After header (delta-seconds
436+ form) if present and sane; the HTTP-date form falls back to the default."""
437+ try :
438+ delay = float (response .headers .get ("Retry-After" , RETRY_DELAY_DEFAULT ))
439+ except ValueError :
440+ return RETRY_DELAY_DEFAULT
441+ return max (RETRY_DELAY_DEFAULT , min (delay , RETRY_DELAY_MAX ))
442+
428443
429444class RequestQueue (threading .Thread ):
430445 """Used to asynchronously send heartbeats.
@@ -539,17 +554,20 @@ def _dispatch_request(self) -> None:
539554 # NOTE: `e.response is not None` matters: Response.__bool__ is
540555 # False for any non-2xx status, so a plain `if e.response` never
541556 # matches an error response.
542- status_code = e .response .status_code if e .response is not None else None
543- if status_code in self .RETRY_STATUS_CODES :
557+ response = e .response
558+ status_code = response .status_code if response is not None else None
559+ if response is not None and status_code in self .RETRY_STATUS_CODES :
544560 # Transient server-side problem (busy, overloaded, restarting
545561 # or behind a flaky proxy) - the request itself is likely
546562 # fine, so keep it in the queue and retry. Heartbeats are safe
547563 # to replay: a duplicate of an already-processed heartbeat
548564 # merges into the last event as a no-op.
565+ delay = _retry_delay (response )
549566 logger .warning (
550- f"Server error { status_code } , will retry: { request .endpoint } "
567+ f"Server error { status_code } , will retry in { delay } s : { request .endpoint } "
551568 )
552- sleep (0.5 )
569+ # stop-aware wait, so a long Retry-After can't block shutdown
570+ self .wait (delay )
553571 return
554572 else :
555573 # Client errors (e.g. HTTP 400 - bad request, see
0 commit comments