diff --git a/src/orb/_base_client.py b/src/orb/_base_client.py index 4d16eaad..0713e656 100644 --- a/src/orb/_base_client.py +++ b/src/orb/_base_client.py @@ -158,7 +158,7 @@ class BasePage(GenericModel, Generic[ModelT]): Methods: has_next_page(): Check if there is another page available - next_page_info(): Get the necesary information to make a request for the next page + next_page_info(): Get the necessary information to make a request for the next page """ _options: FinalRequestOptions = PrivateAttr() @@ -691,15 +691,15 @@ def _calculate_retry_timeout( return retry_after initial_retry_delay = 0.5 - max_retry_delay = 2.0 + max_retry_delay = 8.0 nb_retries = max_retries - remaining_retries # Apply exponential backoff, but not more than the max. - sleep_seconds = min(initial_retry_delay * pow(nb_retries - 1, 2), max_retry_delay) + sleep_seconds = min(initial_retry_delay * pow(2.0, nb_retries), max_retry_delay) # Apply some jitter, plus-or-minus half a second. - jitter = random() - 0.5 - timeout = sleep_seconds + jitter + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter return timeout if timeout >= 0 else 0 def _should_retry(self, response: httpx.Response) -> bool: diff --git a/tests/test_client.py b/tests/test_client.py index c962033a..56ff9c48 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -539,18 +539,20 @@ class Model(BaseModel): "remaining_retries,retry_after,timeout", [ [3, "20", 20], - [3, "0", 2], - [3, "-10", 2], + [3, "0", 0.5], + [3, "-10", 0.5], [3, "60", 60], - [3, "61", 2], + [3, "61", 0.5], [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], - [3, "Fri, 29 Sep 2023 16:26:37 GMT", 2], - [3, "Fri, 29 Sep 2023 16:26:27 GMT", 2], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], - [3, "Fri, 29 Sep 2023 16:27:38 GMT", 2], - [3, "99999999999999999999999999999999999", 2], - [3, "Zun, 29 Sep 2023 16:26:27 GMT", 2], - [3, "", 2], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -558,9 +560,9 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str client = Orb(base_url=base_url, api_key=api_key, _strict_response_validation=True) headers = httpx.Headers({"retry-after": retry_after}) - options = FinalRequestOptions(method="get", url="/foo", max_retries=2) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) - assert calculated == pytest.approx(timeout, 0.6) # pyright: ignore[reportUnknownMemberType] + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] class TestAsyncOrb: @@ -1083,18 +1085,20 @@ class Model(BaseModel): "remaining_retries,retry_after,timeout", [ [3, "20", 20], - [3, "0", 2], - [3, "-10", 2], + [3, "0", 0.5], + [3, "-10", 0.5], [3, "60", 60], - [3, "61", 2], + [3, "61", 0.5], [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], - [3, "Fri, 29 Sep 2023 16:26:37 GMT", 2], - [3, "Fri, 29 Sep 2023 16:26:27 GMT", 2], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], - [3, "Fri, 29 Sep 2023 16:27:38 GMT", 2], - [3, "99999999999999999999999999999999999", 2], - [3, "Zun, 29 Sep 2023 16:26:27 GMT", 2], - [3, "", 2], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -1103,6 +1107,6 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte client = AsyncOrb(base_url=base_url, api_key=api_key, _strict_response_validation=True) headers = httpx.Headers({"retry-after": retry_after}) - options = FinalRequestOptions(method="get", url="/foo", max_retries=2) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) - assert calculated == pytest.approx(timeout, 0.6) # pyright: ignore[reportUnknownMemberType] + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]