diff --git a/openfga_sdk/api_client.py b/openfga_sdk/api_client.py index 4c521bc..079d09d 100644 --- a/openfga_sdk/api_client.py +++ b/openfga_sdk/api_client.py @@ -38,6 +38,9 @@ def random_time(loop_count, min_wait_in_ms) -> float: """ minimum = math.ceil(2**loop_count * min_wait_in_ms) maximum = math.ceil(2 ** (loop_count + 1) * min_wait_in_ms) + # randrange() raises on an empty range, e.g. when min_wait_in_ms is 0 + if maximum <= minimum: + return minimum / 1000 return random.randrange(minimum, maximum) / 1000 diff --git a/openfga_sdk/oauth2_common.py b/openfga_sdk/oauth2_common.py index 71562a6..6eb538c 100644 --- a/openfga_sdk/oauth2_common.py +++ b/openfga_sdk/oauth2_common.py @@ -19,7 +19,11 @@ def jitter(loop_count, min_wait_in_ms): """ minimum = math.ceil(2**loop_count * min_wait_in_ms) maximum = math.ceil(2 ** (loop_count + 1) * min_wait_in_ms) - jitter = random.randrange(minimum, maximum) / 1000 + # randrange() raises on an empty range, e.g. when min_wait_in_ms is 0 + if maximum <= minimum: + jitter = minimum / 1000 + else: + jitter = random.randrange(minimum, maximum) / 1000 # If running in pytest, set jitter to 0 to speed up tests if "pytest" in sys.modules: diff --git a/openfga_sdk/sync/api_client.py b/openfga_sdk/sync/api_client.py index af0e67e..10f15f7 100644 --- a/openfga_sdk/sync/api_client.py +++ b/openfga_sdk/sync/api_client.py @@ -37,6 +37,9 @@ def random_time(loop_count, min_wait_in_ms) -> float: """ minimum = math.ceil(2**loop_count * min_wait_in_ms) maximum = math.ceil(2 ** (loop_count + 1) * min_wait_in_ms) + # randrange() raises on an empty range, e.g. when min_wait_in_ms is 0 + if maximum <= minimum: + return minimum / 1000 return random.randrange(minimum, maximum) / 1000 diff --git a/test/backoff_test.py b/test/backoff_test.py new file mode 100644 index 0000000..d1ed32b --- /dev/null +++ b/test/backoff_test.py @@ -0,0 +1,29 @@ +import unittest + +from openfga_sdk import api_client +from openfga_sdk.oauth2_common import jitter +from openfga_sdk.sync import api_client as sync_api_client + + +class TestBackoff(unittest.TestCase): + """The retry wait helpers accept a minimum wait of 0""" + + def test_random_time_with_zero_min_wait(self): + for loop_count in range(3): + self.assertEqual(api_client.random_time(loop_count, 0), 0) + self.assertEqual(sync_api_client.random_time(loop_count, 0), 0) + + def test_random_time_stays_in_range(self): + for loop_count in range(3): + for random_time in (api_client.random_time, sync_api_client.random_time): + wait = random_time(loop_count, 100) + self.assertGreaterEqual(wait, 2**loop_count * 100 / 1000) + self.assertLess(wait, 2 ** (loop_count + 1) * 100 / 1000) + + def test_jitter_with_zero_min_wait(self): + for loop_count in range(3): + self.assertEqual(jitter(loop_count, 0), 0) + + +if __name__ == "__main__": + unittest.main()