diff --git a/.gitignore b/.gitignore index 3c966caa..b7e6dfdb 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ doc/_build tenacity/_version.py /.pytest_cache +venv/ +__pycache__/ +*.pyc diff --git a/tenacity/wait.py b/tenacity/wait.py index 6607e4fd..ccec4017 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -318,10 +318,14 @@ def __init__( @override def __call__(self, retry_state: "RetryCallState") -> float: - jitter = random.uniform(0, self.jitter) + #jitter = random.uniform(0, self.jitter) try: exp = self.exp_base ** (retry_state.attempt_number - 1) - result = self.multiplier * exp + jitter + result = self.multiplier * exp except OverflowError: result = self.max + # Subtract jitter width from max delay before adding jitter + # It prevents capped retries from syncing up at the same timestamp + result = min(result, max(0, self.max - self.jitter)) + result += random.uniform(0, self.jitter) return max(max(0, self.min), min(result, self.max)) diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 95ab8117..ecaa54bf 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -671,14 +671,19 @@ def test_wait_exponential_jitter(self) -> None: self._assert_inclusive_range(fn(make_retry_state(4, 0)), 8, 9) self._assert_inclusive_range(fn(make_retry_state(5, 0)), 16, 17) self._assert_inclusive_range(fn(make_retry_state(6, 0)), 32, 33) - self.assertEqual(fn(make_retry_state(7, 0)), 60) - self.assertEqual(fn(make_retry_state(8, 0)), 60) - self.assertEqual(fn(make_retry_state(9, 0)), 60) + # Keep jitter active at the cap to prevent synchronized retries + self._assert_inclusive_range(fn(make_retry_state(7, 0)),59, 60) + self._assert_inclusive_range(fn(make_retry_state(8, 0)),59, 60) + self._assert_inclusive_range(fn(make_retry_state(9, 0)),59, 60) with self.assertWarns(DeprecationWarning): fn = tenacity.wait_exponential_jitter(10, 5) - for _ in range(1000): - self.assertEqual(fn(make_retry_state(1, 0)), 5) + results = {fn(make_retry_state(1, 0)) for _ in range(1000)} + # before this every call collapsed at max =5 but now it will spread across [4,5] + # which is [max-jitter, max] + self.assertGreater(len(results), 1) + for r in results: + self._assert_inclusive_range(r, 4, 5) # Default arguments exist fn = tenacity.wait_exponential_jitter() @@ -705,7 +710,7 @@ def test_wait_exponential_jitter_timedelta(self) -> None: for _ in range(1000): self._assert_inclusive_range(fn(make_retry_state(1, 0)), 5, 5) self._assert_inclusive_range(fn(make_retry_state(5, 0)), 16, 17) - self.assertEqual(fn(make_retry_state(7, 0)), 60) + self._assert_inclusive_range(fn(make_retry_state(7, 0)), 59, 60) def test_wait_exponential_jitter_multiplier(self) -> None: fn = tenacity.wait_exponential_jitter(multiplier=10, max=60, jitter=0)