From 2b936a8e604d95acc987632a9469e2210c25943b Mon Sep 17 00:00:00 2001 From: Johnathan Walker Date: Tue, 19 May 2026 09:22:41 -0500 Subject: [PATCH] [ServiceBus] Fix flaky test_backoff_fixed_retry by mocking time.sleep The test previously measured wall-clock time around real time.sleep(0.8) calls and asserted the duration was strictly less than 1.6 seconds. On slow CI agents (notably macOS hosts) the elapsed time can exceed the budget by a few ms (e.g., 1.6399s observed in PR #46953), causing intermittent failures. Mock time.sleep in azure.servicebus._base_handler and assert the value passed to sleep instead. This is deterministic and ~80x faster. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure-servicebus/tests/test_sb_client.py | 76 +++++++------------ 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py index 14dbae7cfc3e..c7643a7e8638 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py +++ b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py @@ -641,56 +641,32 @@ def test_sb_client_with_ssl_context( @pytest.mark.parametrize("uamqp_transport", uamqp_transport_params, ids=uamqp_transport_ids) def test_backoff_fixed_retry(self, uamqp_transport): - - client = ServiceBusClient("fake.host.com", "fake_eh", retry_mode="fixed", uamqp_transport=uamqp_transport) - # queue sender - sender = client.get_queue_sender("fake_name") - backoff = client._config.retry_backoff_factor - start_time = time.time() - sender._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) - sleep_time_fixed = time.time() - start_time - # exp = 0.8 * (2 ** 1) = 1.6 - # time.sleep() in _backoff will take AT LEAST time 'exp' for retry_mode='exponential' - # check that fixed is less than 'exp' - assert sleep_time_fixed < backoff * (2**1) - - # topic sender - sender = client.get_topic_sender("fake_name") - backoff = client._config.retry_backoff_factor - start_time = time.time() - sender._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) - sleep_time_fixed = time.time() - start_time - assert sleep_time_fixed < backoff * (2**1) - - # queue receiver - receiver = client.get_queue_receiver("fake_name") - backoff = client._config.retry_backoff_factor - start_time = time.time() - receiver._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) - sleep_time_fixed = time.time() - start_time - assert sleep_time_fixed < backoff * (2**1) - - # subscription receiver - receiver = client.get_subscription_receiver("fake_topic", "fake_sub") - backoff = client._config.retry_backoff_factor - start_time = time.time() - receiver._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) - sleep_time_fixed = time.time() - start_time - assert sleep_time_fixed < backoff * (2**1) - - client = ServiceBusClient( - "fake.host.com", "fake_eh", retry_mode=RetryMode.Fixed, uamqp_transport=uamqp_transport - ) - # queue sender - sender = client.get_queue_sender("fake_name") - backoff = client._config.retry_backoff_factor - start_time = time.time() - sender._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) - sleep_time_fixed = time.time() - start_time - # exp = 0.8 * (2 ** 1) = 1.6 - # time.sleep() in _backoff will take AT LEAST time 'exp' for retry_mode='exponential' - # check that fixed is less than 'exp' - assert sleep_time_fixed < backoff * (2**1) + # Patch time.sleep inside _base_handler so the test is deterministic and + # not subject to OS scheduling jitter (previously this measured wall-clock + # time around real sleeps which caused flakes on slow CI agents). + from unittest import mock + + def _assert_fixed_backoff(handler): + backoff = handler._config.retry_backoff_factor + with mock.patch("azure.servicebus._base_handler.time.sleep") as mocked_sleep: + handler._backoff(retried_times=1, last_exception=Exception("fake"), abs_timeout_time=None) + # _backoff should sleep exactly once with the fixed backoff value. + mocked_sleep.assert_called_once() + (slept,), _ = mocked_sleep.call_args + # In 'fixed' mode the sleep should equal `backoff` (i.e. 0.8), and in + # particular must be strictly less than what 'exponential' would use + # at retried_times=1: backoff * (2 ** 1). + assert slept == backoff + assert slept < backoff * (2**1) + + for retry_mode in ("fixed", RetryMode.Fixed): + client = ServiceBusClient( + "fake.host.com", "fake_eh", retry_mode=retry_mode, uamqp_transport=uamqp_transport + ) + _assert_fixed_backoff(client.get_queue_sender("fake_name")) + _assert_fixed_backoff(client.get_topic_sender("fake_name")) + _assert_fixed_backoff(client.get_queue_receiver("fake_name")) + _assert_fixed_backoff(client.get_subscription_receiver("fake_topic", "fake_sub")) @pytest.mark.parametrize("uamqp_transport", uamqp_transport_params, ids=uamqp_transport_ids) def test_custom_client_id_queue_sender(self, uamqp_transport, **kwargs):