diff --git a/optimizely/odp/odp_event_manager.py b/optimizely/odp/odp_event_manager.py index 3fb961ac..09269297 100644 --- a/optimizely/odp/odp_event_manager.py +++ b/optimizely/odp/odp_event_manager.py @@ -164,7 +164,7 @@ def _flush_batch(self) -> None: self.logger.debug(f'ODP event queue: flushing batch size {batch_len}.') should_retry = False initial_retry_interval = 0.2 # 200ms - max_retry_interval = 1.0 # 1 second + max_retry_interval = 3.0 # 3 seconds for i in range(1 + self.retry_count): try: diff --git a/tests/test_odp_event_manager.py b/tests/test_odp_event_manager.py index acec396f..0b7e10a3 100644 --- a/tests/test_odp_event_manager.py +++ b/tests/test_odp_event_manager.py @@ -305,6 +305,41 @@ def test_odp_event_manager_retry_success(self, *args): self.assertStrictTrue(event_manager.is_running) event_manager.stop() + def test_odp_event_manager_retry_max_interval_3_seconds(self, *args): + """Verify that max retry interval is capped at 3 seconds.""" + mock_logger = mock.Mock() + event_manager = OdpEventManager(mock_logger) + event_manager.start(self.odp_config) + event_manager.retry_count = 5 # Increase retry count to test higher exponential values + + number_of_tries = event_manager.retry_count + 1 + + with mock.patch.object( + event_manager.api_manager, 'send_odp_events', new_callable=CopyingMock, return_value=True + ) as mock_send, mock.patch('time.sleep') as mock_sleep: + event_manager.send_event(**self.events[0]) + event_manager.flush() + event_manager.event_queue.join() + + mock_send.assert_has_calls( + [mock.call(self.api_key, self.api_host, [self.processed_events[0]])] * number_of_tries + ) + self.assertEqual(len(event_manager._current_batch), 0) + # Verify exponential backoff with 3s cap: 0.2s, 0.4s, 0.8s, 1.6s, 3.0s (capped) + # Delays are: 0.2 * 2^0, 0.2 * 2^1, 0.2 * 2^2, 0.2 * 2^3, min(0.2 * 2^4, 3.0) + mock_sleep.assert_has_calls([ + mock.call(0.2), # 1st retry: 0.2 * 2^0 = 0.2 + mock.call(0.4), # 2nd retry: 0.2 * 2^1 = 0.4 + mock.call(0.8), # 3rd retry: 0.2 * 2^2 = 0.8 + mock.call(1.6), # 4th retry: 0.2 * 2^3 = 1.6 + mock.call(3.0) # 5th retry: min(0.2 * 2^4, 3.0) = min(3.2, 3.0) = 3.0 + ]) + mock_logger.debug.assert_any_call('Error dispatching ODP events, retrying after 3.0s.') + mock_logger.error.assert_called_once_with( + f'ODP event send failed (Failed after 5 retries: {[self.processed_events[0]]}).' + ) + event_manager.stop() + def test_odp_event_manager_send_failure(self, *args): mock_logger = mock.Mock() event_manager = OdpEventManager(mock_logger)