3535 OperationFailure ,
3636)
3737from pymongo .helpers_shared import _REAUTHENTICATION_REQUIRED_CODE
38- from pymongo .lock import _async_create_lock
3938
4039_IS_SYNC = False
4140
@@ -79,8 +78,6 @@ async def inner(*args: Any, **kwargs: Any) -> Any:
7978
8079_BACKOFF_INITIAL = 0.1
8180_BACKOFF_MAX = 10
82- DEFAULT_RETRY_TOKEN_CAPACITY = 1000.0
83- DEFAULT_RETRY_TOKEN_RETURN = 0.1
8481
8582
8683def _backoff (
@@ -90,78 +87,32 @@ def _backoff(
9087 return jitter * min (initial_delay * (2 ** attempt ), max_delay )
9188
9289
93- class _TokenBucket :
94- """A token bucket implementation for rate limiting."""
95-
96- def __init__ (
97- self ,
98- capacity : float = DEFAULT_RETRY_TOKEN_CAPACITY ,
99- return_rate : float = DEFAULT_RETRY_TOKEN_RETURN ,
100- ):
101- self .lock = _async_create_lock ()
102- self .capacity = capacity
103- self .tokens = capacity
104- self .return_rate = return_rate
105-
106- async def consume (self ) -> bool :
107- """Consume a token from the bucket if available."""
108- async with self .lock :
109- if self .tokens >= 1 :
110- self .tokens -= 1
111- return True
112- return False
113-
114- async def deposit (self , retry : bool = False ) -> None :
115- """Deposit a token back into the bucket."""
116- retry_token = 1 if retry else 0
117- async with self .lock :
118- self .tokens = min (self .capacity , self .tokens + retry_token + self .return_rate )
119-
120-
12190class _RetryPolicy :
122- """A retry limiter that performs exponential backoff with jitter.
123-
124- When adaptive retries are enabled, retry attempts are limited by a token bucket to prevent overwhelming the server during
125- a prolonged outage or high load.
126- """
91+ """A retry limiter that performs exponential backoff with jitter."""
12792
12893 def __init__ (
12994 self ,
130- token_bucket : _TokenBucket ,
13195 attempts : int = MAX_ADAPTIVE_RETRIES ,
13296 backoff_initial : float = _BACKOFF_INITIAL ,
13397 backoff_max : float = _BACKOFF_MAX ,
13498 ):
135- self .token_bucket = token_bucket
13699 self .attempts = attempts
137100 self .backoff_initial = backoff_initial
138101 self .backoff_max = backoff_max
139- self .adaptive_retry = False
140-
141- async def record_success (self , retry : bool ) -> None :
142- """Record a successful operation."""
143- if self .adaptive_retry :
144- await self .token_bucket .deposit (retry )
145102
146103 def backoff (self , attempt : int ) -> float :
147- """Return the backoff duration for the given ."""
104+ """Return the backoff duration for the given attempt ."""
148105 return _backoff (max (0 , attempt - 1 ), self .backoff_initial , self .backoff_max )
149106
150107 async def should_retry (self , attempt : int , delay : float ) -> bool :
151- """Return if we have budget to retry and how long to backoff."""
108+ """Return if we have retry attempts remaining and the next backoff would not exceed a timeout ."""
152109 if attempt > self .attempts :
153110 return False
154111
155- # If the delay would exceed the deadline, bail early before consuming a token.
156112 if _csot .get_timeout ():
157113 if time .monotonic () + delay > _csot .get_deadline ():
158114 return False
159115
160- # Check token bucket last since we only want to consume a token if we actually retry.
161- if self .adaptive_retry and not await self .token_bucket .consume ():
162- # DRIVERS-3246 Improve diagnostics when this case happens.
163- # We could add info to the exception and log.
164- return False
165116 return True
166117
167118
0 commit comments