Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improves wait_exponential_jitter #426

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
features:
- |
Improves wait_exponential_jitter to inherit wait_exponential
- Reuses `wait_exponential.__call__` method instead of duplicating code
- Follows argument names of wait_exponential
- Adds `min` argument
- Supports supplying `max`, `jitter`, `min` arguments as `timedelta`
43 changes: 25 additions & 18 deletions tenacity/wait.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import abc
import random
import typing
import warnings

from tenacity import _utils

Expand Down Expand Up @@ -194,35 +195,41 @@ def __call__(self, retry_state: "RetryCallState") -> float:
return random.uniform(0, high)


class wait_exponential_jitter(wait_base):
class wait_exponential_jitter(wait_exponential):
"""Wait strategy that applies exponential backoff and jitter.

It allows for a customized initial wait, maximum wait and jitter.
It allows for a customized multiplier, max wait, jitter and min wait.

This implements the strategy described here:
https://cloud.google.com/storage/docs/retry-strategy

The wait time is min(initial * 2**n + random.uniform(0, jitter), maximum)
The wait time is max(min, min(multiplier * 2**n + random.uniform(0, jitter), max))
where n is the retry count.
"""

def __init__(
self,
initial: float = 1,
max: float = _utils.MAX_WAIT, # noqa
exp_base: float = 2,
jitter: float = 1,
multiplier: typing.Union[int, float] = 1,
max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa
exp_base: typing.Union[int, float] = 2,
jitter: _utils.time_unit_type = 1,
min: _utils.time_unit_type = 0, # noqa
initial: typing.Union[int, float, None] = None,
) -> None:
self.initial = initial
self.max = max
self.exp_base = exp_base
self.jitter = jitter
if initial is not None and multiplier != 1:
msg = (
"Received both `multiplier` and `initial` arguments. "
"`initial` is deprecated, use `multiplier` instead."
)
raise ValueError(msg)
elif initial is not None:
msg = "`initial` is deprecated, use `multiplier` instead."
warnings.warn(msg, DeprecationWarning)
multiplier = jitter

super().__init__(multiplier, max, exp_base, min)
self.jitter = _utils.to_seconds(jitter)

def __call__(self, retry_state: "RetryCallState") -> float:
jitter = random.uniform(0, self.jitter)
try:
exp = self.exp_base ** (retry_state.attempt_number - 1)
result = self.initial * exp + jitter
except OverflowError:
result = self.max
return max(0, min(result, self.max))
result = super().__call__(retry_state) + random.uniform(0, self.jitter)
return max(self.min, min(result, self.max))
10 changes: 8 additions & 2 deletions tests/test_tenacity.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,15 @@ def test_wait_exponential_jitter(self):
self.assertEqual(fn(make_retry_state(8, 0)), 60)
self.assertEqual(fn(make_retry_state(9, 0)), 60)

fn = tenacity.wait_exponential_jitter(10, 5)
max = 5
fn = tenacity.wait_exponential_jitter(10, max)
for _ in range(1000):
self.assertEqual(fn(make_retry_state(1, 0)), 5)
self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0, max)

min = 5
fn = tenacity.wait_exponential_jitter(min=min)
for _ in range(1000):
self._assert_inclusive_range(fn(make_retry_state(1, 0)), min, 6)

# Default arguments exist
fn = tenacity.wait_exponential_jitter()
Expand Down
Loading