Skip to content

Commit cd572a3

Browse files
committed
Revert "[FSSDK-12150] [Ruby] Add Event Retries (#385)"
This reverts commit 684fdac.
1 parent 684fdac commit cd572a3

6 files changed

Lines changed: 31 additions & 233 deletions

File tree

lib/optimizely/event/batch_event_processor.rb

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -172,35 +172,20 @@ def flush_queue!
172172
return if @current_batch.empty?
173173

174174
log_event = Optimizely::EventFactory.create_log_event(@current_batch, @logger)
175-
@logger.log(
176-
Logger::INFO,
177-
'Flushing Queue.'
178-
)
179-
180-
retry_count = 0
181-
max_retries = Optimizely::Helpers::Constants::EVENT_DISPATCH_CONFIG[:MAX_RETRIES]
182-
183-
while retry_count < max_retries
184-
begin
185-
@event_dispatcher.dispatch_event(log_event)
186-
@notification_center&.send_notifications(
187-
NotificationCenter::NOTIFICATION_TYPES[:LOG_EVENT],
188-
log_event
189-
)
190-
# Success - break out of retry loop
191-
break
192-
rescue StandardError => e
193-
@logger.log(Logger::ERROR, "Error dispatching event: #{log_event} #{e.message}.")
194-
retry_count += 1
195-
196-
if retry_count < max_retries
197-
delay = calculate_retry_interval(retry_count - 1)
198-
@logger.log(Logger::DEBUG, "Retrying event dispatch (attempt #{retry_count + 1} of #{max_retries}) after #{delay}s")
199-
sleep(delay)
200-
end
201-
end
175+
begin
176+
@logger.log(
177+
Logger::INFO,
178+
'Flushing Queue.'
179+
)
180+
181+
@event_dispatcher.dispatch_event(log_event)
182+
@notification_center&.send_notifications(
183+
NotificationCenter::NOTIFICATION_TYPES[:LOG_EVENT],
184+
log_event
185+
)
186+
rescue StandardError => e
187+
@logger.log(Logger::ERROR, "Error dispatching event: #{log_event} #{e.message}.")
202188
end
203-
204189
@current_batch = []
205190
end
206191

@@ -246,16 +231,5 @@ def positive_number?(value)
246231
# false otherwise.
247232
Helpers::Validator.finite_number?(value) && value.positive?
248233
end
249-
250-
# Calculate exponential backoff interval: 200ms, 400ms, 800ms, ... capped at 1s
251-
#
252-
# @param retry_count - Zero-based retry count
253-
# @return [Float] - Delay in seconds
254-
def calculate_retry_interval(retry_count)
255-
initial_interval = Helpers::Constants::EVENT_DISPATCH_CONFIG[:INITIAL_RETRY_INTERVAL]
256-
max_interval = Helpers::Constants::EVENT_DISPATCH_CONFIG[:MAX_RETRY_INTERVAL]
257-
interval = initial_interval * (2**retry_count)
258-
[interval, max_interval].min
259-
end
260234
end
261235
end

lib/optimizely/helpers/constants.rb

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -459,10 +459,7 @@ module Constants
459459
}.freeze
460460

461461
EVENT_DISPATCH_CONFIG = {
462-
REQUEST_TIMEOUT: 10,
463-
MAX_RETRIES: 3,
464-
INITIAL_RETRY_INTERVAL: 0.2, # 200ms in seconds
465-
MAX_RETRY_INTERVAL: 1.0 # 1 second
462+
REQUEST_TIMEOUT: 10
466463
}.freeze
467464

468465
ODP_GRAPHQL_API_CONFIG = {
@@ -493,9 +490,7 @@ module Constants
493490
DEFAULT_QUEUE_CAPACITY: 10_000,
494491
DEFAULT_BATCH_SIZE: 10,
495492
DEFAULT_FLUSH_INTERVAL_SECONDS: 1,
496-
DEFAULT_RETRY_COUNT: 3,
497-
INITIAL_RETRY_INTERVAL: 0.2, # 200ms in seconds
498-
MAX_RETRY_INTERVAL: 1.0 # 1 second
493+
DEFAULT_RETRY_COUNT: 3
499494
}.freeze
500495

501496
HTTP_HEADERS = {

lib/optimizely/odp/odp_event_manager.rb

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,7 @@ def flush_batch!
239239
end
240240
break unless should_retry
241241

242-
if i < @retry_count - 1
243-
# Exponential backoff: 200ms, 400ms, 800ms, ... capped at 1s
244-
delay = calculate_retry_interval(i)
245-
@logger.log(Logger::DEBUG, "Error dispatching ODP events, retrying (attempt #{i + 2} of #{@retry_count}) after #{delay}s")
246-
sleep(delay)
247-
end
242+
@logger.log(Logger::DEBUG, 'Error dispatching ODP events, scheduled to retry.') if i < @retry_count
248243
i += 1
249244
end
250245

@@ -287,16 +282,5 @@ def process_config_update
287282
@api_key = @odp_config&.api_key
288283
@api_host = @odp_config&.api_host
289284
end
290-
291-
# Calculate exponential backoff interval: 200ms, 400ms, 800ms, ... capped at 1s
292-
#
293-
# @param retry_count - Zero-based retry count
294-
# @return [Float] - Delay in seconds
295-
def calculate_retry_interval(retry_count)
296-
initial_interval = Helpers::Constants::ODP_EVENT_MANAGER[:INITIAL_RETRY_INTERVAL]
297-
max_interval = Helpers::Constants::ODP_EVENT_MANAGER[:MAX_RETRY_INTERVAL]
298-
interval = initial_interval * (2**retry_count)
299-
[interval, max_interval].min
300-
end
301285
end
302286
end

spec/event/batch_event_processor_spec.rb

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,9 @@
293293
@event_processor.flush
294294
# Wait until other thread has processed the event.
295295
sleep 0.1 until @event_processor.current_batch.empty?
296-
sleep 0.7 # Wait for retries to complete (200ms + 400ms + processing time)
297296

298297
expect(@notification_center).not_to have_received(:send_notifications)
299-
# With retries, error will be logged 3 times (once per attempt)
300-
expect(spy_logger).to have_received(:log).exactly(3).times.with(
298+
expect(spy_logger).to have_received(:log).once.with(
301299
Logger::ERROR,
302300
"Error dispatching event: #{log_event} Timeout::Error."
303301
)
@@ -379,93 +377,4 @@
379377
expect(@event_processor.event_queue.length).to eq(0)
380378
expect(spy_logger).to have_received(:log).with(Logger::WARN, 'Executor shutdown, not accepting tasks.').once
381379
end
382-
383-
context 'retry logic with exponential backoff' do
384-
it 'should retry on dispatch errors with exponential backoff' do
385-
@event_processor = Optimizely::BatchEventProcessor.new(
386-
event_dispatcher: @event_dispatcher,
387-
batch_size: 1,
388-
flush_interval: 10_000,
389-
logger: spy_logger
390-
)
391-
392-
user_event = Optimizely::UserEventFactory.create_conversion_event(project_config, event, 'test_user', nil, nil)
393-
log_event = Optimizely::EventFactory.create_log_event(user_event, spy_logger)
394-
395-
# Simulate dispatch failure twice, then success
396-
call_count = 0
397-
allow(@event_dispatcher).to receive(:dispatch_event) do
398-
call_count += 1
399-
raise StandardError, 'Network error' if call_count < 3
400-
end
401-
402-
start_time = Time.now
403-
@event_processor.process(user_event)
404-
405-
# Wait for processing to complete
406-
sleep 0.1 until @event_processor.event_queue.empty?
407-
sleep 0.7 # Wait for retries to complete (200ms + 400ms + processing time)
408-
409-
elapsed_time = Time.now - start_time
410-
411-
# Should make 3 attempts total (1 initial + 2 retries)
412-
expect(@event_dispatcher).to have_received(:dispatch_event).with(log_event).exactly(3).times
413-
414-
# Should have delays: 200ms + 400ms = 600ms minimum
415-
expect(elapsed_time).to be >= 0.6
416-
417-
# Should log retry attempts
418-
expect(spy_logger).to have_received(:log).with(
419-
Logger::DEBUG, /Retrying event dispatch/
420-
).at_least(:twice)
421-
end
422-
423-
it 'should give up after max retries' do
424-
@event_processor = Optimizely::BatchEventProcessor.new(
425-
event_dispatcher: @event_dispatcher,
426-
batch_size: 1,
427-
flush_interval: 10_000,
428-
logger: spy_logger
429-
)
430-
431-
user_event = Optimizely::UserEventFactory.create_conversion_event(project_config, event, 'test_user', nil, nil)
432-
log_event = Optimizely::EventFactory.create_log_event(user_event, spy_logger)
433-
434-
# Simulate dispatch failure every time
435-
allow(@event_dispatcher).to receive(:dispatch_event).and_raise(StandardError, 'Network error')
436-
437-
@event_processor.process(user_event)
438-
439-
# Wait for processing to complete
440-
sleep 0.1 until @event_processor.event_queue.empty?
441-
sleep 0.7 # Wait for all retries to complete
442-
443-
# Should make 3 attempts total (1 initial + 2 retries)
444-
expect(@event_dispatcher).to have_received(:dispatch_event).with(log_event).exactly(3).times
445-
446-
# Should log error for each attempt
447-
expect(spy_logger).to have_received(:log).with(
448-
Logger::ERROR, /Error dispatching event/
449-
).exactly(3).times
450-
end
451-
452-
it 'should calculate correct exponential backoff intervals' do
453-
processor = Optimizely::BatchEventProcessor.new
454-
455-
# First retry: 200ms
456-
expect(processor.send(:calculate_retry_interval, 0)).to eq(0.2)
457-
458-
# Second retry: 400ms
459-
expect(processor.send(:calculate_retry_interval, 1)).to eq(0.4)
460-
461-
# Third retry: 800ms
462-
expect(processor.send(:calculate_retry_interval, 2)).to eq(0.8)
463-
464-
# Fourth retry: capped at 1s
465-
expect(processor.send(:calculate_retry_interval, 3)).to eq(1.0)
466-
467-
# Fifth retry: still capped at 1s
468-
expect(processor.send(:calculate_retry_interval, 4)).to eq(1.0)
469-
end
470-
end
471380
end

spec/event_dispatcher_spec.rb

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,16 @@
4747

4848
it 'should pass the proxy_config to the HttpUtils helper class' do
4949
event = Optimizely::Event.new(:post, @url, @params, @post_headers)
50-
# Allow the method to be called (potentially multiple times due to retries)
51-
allow(Optimizely::Helpers::HttpUtils).to receive(:make_request).with(
50+
expect(Optimizely::Helpers::HttpUtils).to receive(:make_request).with(
5251
event.url,
5352
event.http_verb,
5453
event.params.to_json,
5554
event.headers,
5655
Optimizely::Helpers::Constants::EVENT_DISPATCH_CONFIG[:REQUEST_TIMEOUT],
5756
proxy_config
58-
).and_return(double(code: '200'))
57+
)
5958

6059
@customized_event_dispatcher.dispatch_event(event)
61-
62-
# Verify it was called at least once with the correct parameters
63-
expect(Optimizely::Helpers::HttpUtils).to have_received(:make_request).with(
64-
event.url,
65-
event.http_verb,
66-
event.params.to_json,
67-
event.headers,
68-
Optimizely::Helpers::Constants::EVENT_DISPATCH_CONFIG[:REQUEST_TIMEOUT],
69-
proxy_config
70-
).at_least(:once)
7160
end
7261
end
7362

@@ -182,19 +171,21 @@
182171
stub_request(:post, @url).to_return(status: 399)
183172
event = Optimizely::Event.new(:post, @url, @params, @post_headers)
184173

185-
@customized_event_dispatcher.dispatch_event(event)
174+
response = @customized_event_dispatcher.dispatch_event(event)
186175

187-
expect(spy_logger).to have_received(:log).with(Logger::DEBUG, 'event successfully sent with response code 399')
176+
expect(response).to have_received(:log)
177+
expect(spy_logger).to have_received(:log)
188178
expect(error_handler).to_not have_received(:handle_error)
189179
end
190180

191181
it 'should do nothing on response with status code 600' do
192182
stub_request(:post, @url).to_return(status: 600)
193183
event = Optimizely::Event.new(:post, @url, @params, @post_headers)
194184

195-
@customized_event_dispatcher.dispatch_event(event)
185+
response = @customized_event_dispatcher.dispatch_event(event)
196186

197-
expect(spy_logger).to have_received(:log).with(Logger::DEBUG, 'event successfully sent with response code 600')
187+
expect(response).to have_received(:log)
188+
expect(spy_logger).to have_received(:log)
198189
expect(error_handler).not_to have_received(:handle_error)
199190
end
200191
end

spec/odp/odp_event_manager_spec.rb

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -260,20 +260,16 @@
260260
allow(SecureRandom).to receive(:uuid).and_return(test_uuid)
261261
event_manager = Optimizely::OdpEventManager.new(logger: spy_logger)
262262
retry_count = event_manager.instance_variable_get('@retry_count')
263-
allow(event_manager.api_manager).to receive(:send_odp_events).exactly(retry_count).times.with(api_key, api_host, odp_events).and_return(true)
263+
allow(event_manager.api_manager).to receive(:send_odp_events).exactly(retry_count + 1).times.with(api_key, api_host, odp_events).and_return(true)
264264
event_manager.start!(odp_config)
265265

266266
event_manager.send_event(**events[0])
267267
event_manager.send_event(**events[1])
268268
event_manager.flush
269-
# Need to wait longer for retries with exponential backoff (200ms + 400ms = 600ms)
270-
sleep(1) until event_manager.instance_variable_get('@event_queue').empty?
269+
sleep(0.1) until event_manager.instance_variable_get('@event_queue').empty?
271270

272271
expect(event_manager.instance_variable_get('@current_batch').length).to eq 0
273-
# Updated log message includes retry attempt and delay info
274-
expect(spy_logger).to have_received(:log).with(
275-
Logger::DEBUG, /Error dispatching ODP events, retrying/
276-
).exactly(retry_count - 1).times
272+
expect(spy_logger).to have_received(:log).exactly(retry_count).times.with(Logger::DEBUG, 'Error dispatching ODP events, scheduled to retry.')
277273
expect(spy_logger).to have_received(:log).once.with(Logger::ERROR, "ODP event send failed (Failed after 3 retries: #{processed_events.to_json}).")
278274

279275
event_manager.stop!
@@ -282,20 +278,16 @@
282278
it 'should retry on network failure' do
283279
allow(SecureRandom).to receive(:uuid).and_return(test_uuid)
284280
event_manager = Optimizely::OdpEventManager.new(logger: spy_logger)
285-
allow(event_manager.api_manager).to receive(:send_odp_events).with(api_key, api_host, odp_events).and_return(true, true, false)
281+
allow(event_manager.api_manager).to receive(:send_odp_events).once.with(api_key, api_host, odp_events).and_return(true, true, false)
286282
event_manager.start!(odp_config)
287283

288284
event_manager.send_event(**events[0])
289285
event_manager.send_event(**events[1])
290286
event_manager.flush
291-
# Need to wait longer for retries with exponential backoff (200ms + 400ms = 600ms)
292-
sleep(1) until event_manager.instance_variable_get('@event_queue').empty?
287+
sleep(0.1) until event_manager.instance_variable_get('@event_queue').empty?
293288

294289
expect(event_manager.instance_variable_get('@current_batch').length).to eq 0
295-
# Updated log message includes retry attempt and delay info
296-
expect(spy_logger).to have_received(:log).with(
297-
Logger::DEBUG, /Error dispatching ODP events, retrying/
298-
).twice
290+
expect(spy_logger).to have_received(:log).twice.with(Logger::DEBUG, 'Error dispatching ODP events, scheduled to retry.')
299291
expect(spy_logger).not_to have_received(:log).with(Logger::ERROR, anything)
300292
expect(event_manager.running?).to be true
301293
event_manager.stop!
@@ -547,52 +539,5 @@
547539
expect(spy_logger).to have_received(:log).once.with(Logger::DEBUG, 'ODP event queue: cannot send before config has been set.')
548540
expect(spy_logger).not_to have_received(:log).with(Logger::ERROR, anything)
549541
end
550-
551-
it 'should use exponential backoff between retries' do
552-
allow(SecureRandom).to receive(:uuid).and_return(test_uuid)
553-
event_manager = Optimizely::OdpEventManager.new(logger: spy_logger)
554-
555-
# All requests fail to trigger retries
556-
allow(event_manager.api_manager).to receive(:send_odp_events).with(api_key, api_host, odp_events).and_return(true)
557-
event_manager.start!(odp_config)
558-
559-
start_time = Time.now
560-
event_manager.send_event(**events[0])
561-
event_manager.send_event(**events[1])
562-
event_manager.flush
563-
564-
# Wait for all retries to complete (need at least 600ms for 200ms + 400ms delays)
565-
sleep(1) until event_manager.instance_variable_get('@event_queue').empty?
566-
elapsed_time = Time.now - start_time
567-
568-
# Should have delays: 200ms + 400ms = 600ms minimum for 3 total attempts
569-
expect(elapsed_time).to be >= 0.5 # Allow some tolerance
570-
571-
# Should log retry attempts with delay info
572-
expect(spy_logger).to have_received(:log).with(
573-
Logger::DEBUG, /retrying \(attempt \d+ of \d+\) after/
574-
).at_least(:once)
575-
576-
event_manager.stop!
577-
end
578-
579-
it 'should calculate correct exponential backoff intervals' do
580-
event_manager = Optimizely::OdpEventManager.new
581-
582-
# First retry: 200ms
583-
expect(event_manager.send(:calculate_retry_interval, 0)).to eq(0.2)
584-
585-
# Second retry: 400ms
586-
expect(event_manager.send(:calculate_retry_interval, 1)).to eq(0.4)
587-
588-
# Third retry: 800ms
589-
expect(event_manager.send(:calculate_retry_interval, 2)).to eq(0.8)
590-
591-
# Fourth retry: capped at 1s
592-
expect(event_manager.send(:calculate_retry_interval, 3)).to eq(1.0)
593-
594-
# Fifth retry: still capped at 1s
595-
expect(event_manager.send(:calculate_retry_interval, 4)).to eq(1.0)
596-
end
597542
end
598543
end

0 commit comments

Comments
 (0)