optimizely-sdk 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +202 -202
  3. data/lib/optimizely/audience.rb +127 -127
  4. data/lib/optimizely/bucketer.rb +156 -156
  5. data/lib/optimizely/condition_tree_evaluator.rb +123 -123
  6. data/lib/optimizely/config/datafile_project_config.rb +558 -558
  7. data/lib/optimizely/config/proxy_config.rb +34 -34
  8. data/lib/optimizely/config_manager/async_scheduler.rb +95 -95
  9. data/lib/optimizely/config_manager/http_project_config_manager.rb +340 -340
  10. data/lib/optimizely/config_manager/project_config_manager.rb +25 -25
  11. data/lib/optimizely/config_manager/static_project_config_manager.rb +55 -55
  12. data/lib/optimizely/decide/optimizely_decide_option.rb +28 -28
  13. data/lib/optimizely/decide/optimizely_decision.rb +60 -60
  14. data/lib/optimizely/decide/optimizely_decision_message.rb +26 -26
  15. data/lib/optimizely/decision_service.rb +589 -563
  16. data/lib/optimizely/error_handler.rb +39 -39
  17. data/lib/optimizely/event/batch_event_processor.rb +235 -235
  18. data/lib/optimizely/event/entity/conversion_event.rb +44 -44
  19. data/lib/optimizely/event/entity/decision.rb +38 -38
  20. data/lib/optimizely/event/entity/event_batch.rb +86 -86
  21. data/lib/optimizely/event/entity/event_context.rb +50 -50
  22. data/lib/optimizely/event/entity/impression_event.rb +48 -48
  23. data/lib/optimizely/event/entity/snapshot.rb +33 -33
  24. data/lib/optimizely/event/entity/snapshot_event.rb +48 -48
  25. data/lib/optimizely/event/entity/user_event.rb +22 -22
  26. data/lib/optimizely/event/entity/visitor.rb +36 -36
  27. data/lib/optimizely/event/entity/visitor_attribute.rb +38 -38
  28. data/lib/optimizely/event/event_factory.rb +156 -156
  29. data/lib/optimizely/event/event_processor.rb +25 -25
  30. data/lib/optimizely/event/forwarding_event_processor.rb +44 -44
  31. data/lib/optimizely/event/user_event_factory.rb +88 -88
  32. data/lib/optimizely/event_builder.rb +221 -221
  33. data/lib/optimizely/event_dispatcher.rb +69 -69
  34. data/lib/optimizely/exceptions.rb +193 -193
  35. data/lib/optimizely/helpers/constants.rb +459 -459
  36. data/lib/optimizely/helpers/date_time_utils.rb +30 -30
  37. data/lib/optimizely/helpers/event_tag_utils.rb +132 -132
  38. data/lib/optimizely/helpers/group.rb +31 -31
  39. data/lib/optimizely/helpers/http_utils.rb +68 -68
  40. data/lib/optimizely/helpers/sdk_settings.rb +61 -61
  41. data/lib/optimizely/helpers/validator.rb +236 -236
  42. data/lib/optimizely/helpers/variable_type.rb +67 -67
  43. data/lib/optimizely/logger.rb +46 -46
  44. data/lib/optimizely/notification_center.rb +174 -174
  45. data/lib/optimizely/notification_center_registry.rb +71 -71
  46. data/lib/optimizely/odp/lru_cache.rb +114 -114
  47. data/lib/optimizely/odp/odp_config.rb +102 -102
  48. data/lib/optimizely/odp/odp_event.rb +75 -75
  49. data/lib/optimizely/odp/odp_event_api_manager.rb +70 -70
  50. data/lib/optimizely/odp/odp_event_manager.rb +286 -286
  51. data/lib/optimizely/odp/odp_manager.rb +159 -159
  52. data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -122
  53. data/lib/optimizely/odp/odp_segment_manager.rb +97 -97
  54. data/lib/optimizely/optimizely_config.rb +273 -273
  55. data/lib/optimizely/optimizely_factory.rb +183 -184
  56. data/lib/optimizely/optimizely_user_context.rb +238 -238
  57. data/lib/optimizely/params.rb +31 -31
  58. data/lib/optimizely/project_config.rb +99 -99
  59. data/lib/optimizely/semantic_version.rb +166 -166
  60. data/lib/optimizely/user_condition_evaluator.rb +391 -391
  61. data/lib/optimizely/user_profile_service.rb +35 -35
  62. data/lib/optimizely/user_profile_tracker.rb +64 -0
  63. data/lib/optimizely/version.rb +21 -21
  64. data/lib/optimizely.rb +1326 -1262
  65. metadata +8 -5
@@ -1,286 +1,286 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright 2019, 2022, Optimizely and contributors
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
- require_relative 'odp_event_api_manager'
19
- require_relative '../helpers/constants'
20
- require_relative 'odp_event'
21
-
22
- module Optimizely
23
- class OdpEventManager
24
- # Events passed to the OdpEventManager are immediately added to an EventQueue.
25
- # The OdpEventManager maintains a single consumer thread that pulls events off of
26
- # the BlockingQueue and buffers them for either a configured batch size or for a
27
- # maximum duration before the resulting OdpEvent is sent to Odp.
28
-
29
- attr_reader :batch_size, :api_manager, :logger
30
- attr_accessor :odp_config
31
-
32
- def initialize(
33
- api_manager: nil,
34
- logger: NoOpLogger.new,
35
- proxy_config: nil,
36
- request_timeout: nil,
37
- flush_interval: nil
38
- )
39
- @odp_config = nil
40
- @api_host = nil
41
- @api_key = nil
42
-
43
- @mutex = Mutex.new
44
- @event_queue = SizedQueue.new(Optimizely::Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_QUEUE_CAPACITY])
45
- @queue_capacity = Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_QUEUE_CAPACITY]
46
- # received signal should be sent after adding item to event_queue
47
- @received = ConditionVariable.new
48
- @logger = logger
49
- @api_manager = api_manager || OdpEventApiManager.new(logger: @logger, proxy_config: proxy_config, timeout: request_timeout)
50
- @flush_interval = flush_interval || Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_FLUSH_INTERVAL_SECONDS]
51
- @batch_size = @flush_interval&.zero? ? 1 : Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_BATCH_SIZE]
52
- @flush_deadline = 0
53
- @retry_count = Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_RETRY_COUNT]
54
- # current_batch should only be accessed by processing thread
55
- @current_batch = []
56
- @thread = nil
57
- @thread_exception = false
58
- end
59
-
60
- def start!(odp_config)
61
- if running?
62
- @logger.log(Logger::WARN, 'Service already started.')
63
- return
64
- end
65
-
66
- @odp_config = odp_config
67
- @api_host = odp_config.api_host
68
- @api_key = odp_config.api_key
69
-
70
- @thread = Thread.new { run }
71
- @logger.log(Logger::INFO, 'Starting scheduler.')
72
- end
73
-
74
- def flush
75
- begin
76
- @event_queue.push(:FLUSH_SIGNAL, true)
77
- rescue ThreadError
78
- @logger.log(Logger::ERROR, 'Error flushing ODP event queue.')
79
- return
80
- end
81
-
82
- @mutex.synchronize do
83
- @received.signal
84
- end
85
- end
86
-
87
- def update_config
88
- begin
89
- # Adds update config signal to event_queue.
90
- @event_queue.push(:UPDATE_CONFIG, true)
91
- rescue ThreadError
92
- @logger.log(Logger::ERROR, 'Error updating ODP config for the event queue')
93
- end
94
-
95
- @mutex.synchronize do
96
- @received.signal
97
- end
98
- end
99
-
100
- def dispatch(event)
101
- if @thread_exception
102
- @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], 'Queue is down'))
103
- return
104
- end
105
-
106
- # if the processor has been explicitly stopped. Don't accept tasks
107
- unless running?
108
- @logger.log(Logger::WARN, 'ODP event queue is shutdown, not accepting events.')
109
- return
110
- end
111
-
112
- begin
113
- @logger.log(Logger::DEBUG, 'ODP event queue: adding event.')
114
- @event_queue.push(event, true)
115
- rescue => e
116
- @logger.log(Logger::WARN, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], e.message))
117
- return
118
- end
119
-
120
- @mutex.synchronize do
121
- @received.signal
122
- end
123
- end
124
-
125
- def send_event(type:, action:, identifiers:, data:)
126
- case @odp_config&.odp_state
127
- when nil
128
- @logger.log(Logger::DEBUG, 'ODP event queue: cannot send before config has been set.')
129
- return
130
- when OdpConfig::ODP_CONFIG_STATE[:UNDETERMINED]
131
- @logger.log(Logger::DEBUG, 'ODP event queue: cannot send before the datafile has loaded.')
132
- return
133
- when OdpConfig::ODP_CONFIG_STATE[:NOT_INTEGRATED]
134
- @logger.log(Logger::DEBUG, Helpers::Constants::ODP_LOGS[:ODP_NOT_INTEGRATED])
135
- return
136
- end
137
-
138
- event = Optimizely::OdpEvent.new(type: type, action: action, identifiers: identifiers, data: data)
139
- dispatch(event)
140
- end
141
-
142
- def stop!
143
- return unless running?
144
-
145
- begin
146
- @event_queue.push(:SHUTDOWN_SIGNAL, true)
147
- rescue ThreadError
148
- @logger.log(Logger::ERROR, 'Error stopping ODP event queue.')
149
- return
150
- end
151
-
152
- @event_queue.close
153
-
154
- @mutex.synchronize do
155
- @received.signal
156
- end
157
-
158
- @logger.log(Logger::INFO, 'Stopping ODP event queue.')
159
-
160
- @thread.join
161
-
162
- @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], @current_batch.to_json)) unless @current_batch.empty?
163
- end
164
-
165
- def running?
166
- !!@thread && !!@thread.status && !@event_queue.closed?
167
- end
168
-
169
- private
170
-
171
- def run
172
- loop do
173
- @mutex.synchronize do
174
- @received.wait(@mutex, queue_timeout) if @event_queue.empty?
175
- end
176
-
177
- begin
178
- item = @event_queue.pop(true)
179
- rescue ThreadError => e
180
- raise unless e.message == 'queue empty'
181
-
182
- item = nil
183
- end
184
-
185
- case item
186
- when :SHUTDOWN_SIGNAL
187
- @logger.log(Logger::DEBUG, 'ODP event queue: received shutdown signal.')
188
- break
189
-
190
- when :FLUSH_SIGNAL
191
- @logger.log(Logger::DEBUG, 'ODP event queue: received flush signal.')
192
- flush_batch!
193
-
194
- when :UPDATE_CONFIG
195
- @logger.log(Logger::DEBUG, 'ODP event queue: received update config signal.')
196
- process_config_update
197
-
198
- when Optimizely::OdpEvent
199
- add_to_batch(item)
200
-
201
- when nil && !@current_batch.empty?
202
- @logger.log(Logger::DEBUG, 'ODP event queue: flushing on interval.')
203
- flush_batch!
204
- end
205
- end
206
- rescue SignalException
207
- @thread_exception = true
208
- @logger.log(Logger::ERROR, 'Interrupted while processing ODP events.')
209
- rescue => e
210
- @thread_exception = true
211
- @logger.log(Logger::ERROR, "Uncaught exception processing ODP events. Error: #{e.message}")
212
- ensure
213
- @logger.log(Logger::INFO, 'Exiting ODP processing loop. Attempting to flush pending events.')
214
- flush_batch!
215
- end
216
-
217
- def flush_batch!
218
- if @current_batch.empty?
219
- @logger.log(Logger::DEBUG, 'ODP event queue: nothing to flush.')
220
- return
221
- end
222
-
223
- if @api_key.nil? || @api_host.nil?
224
- @logger.log(Logger::DEBUG, Helpers::Constants::ODP_LOGS[:ODP_NOT_INTEGRATED])
225
- @current_batch.clear
226
- return
227
- end
228
-
229
- @logger.log(Logger::DEBUG, "ODP event queue: flushing batch size #{@current_batch.length}.")
230
- should_retry = false
231
-
232
- i = 0
233
- while i < @retry_count
234
- begin
235
- should_retry = @api_manager.send_odp_events(@api_key, @api_host, @current_batch)
236
- rescue StandardError => e
237
- should_retry = false
238
- @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], "Error: #{e.message} #{@current_batch.to_json}"))
239
- end
240
- break unless should_retry
241
-
242
- @logger.log(Logger::DEBUG, 'Error dispatching ODP events, scheduled to retry.') if i < @retry_count
243
- i += 1
244
- end
245
-
246
- @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], "Failed after #{i} retries: #{@current_batch.to_json}")) if should_retry
247
-
248
- @current_batch.clear
249
- end
250
-
251
- def add_to_batch(event)
252
- set_flush_deadline if @current_batch.empty?
253
-
254
- @current_batch << event
255
- return unless @current_batch.length >= @batch_size
256
-
257
- @logger.log(Logger::DEBUG, 'ODP event queue: flushing on batch size.')
258
- flush_batch!
259
- end
260
-
261
- def set_flush_deadline
262
- # Sets time that next flush will occur.
263
- @flush_deadline = Time.new + @flush_interval
264
- end
265
-
266
- def time_till_flush
267
- # Returns seconds until next flush; no less than 0.
268
- [0, @flush_deadline - Time.new].max
269
- end
270
-
271
- def queue_timeout
272
- # Returns seconds until next flush or None if current batch is empty.
273
- return nil if @current_batch.empty?
274
-
275
- time_till_flush
276
- end
277
-
278
- def process_config_update
279
- # Updates the configuration used to send events.
280
- flush_batch! unless @current_batch.empty?
281
-
282
- @api_key = @odp_config&.api_key
283
- @api_host = @odp_config&.api_host
284
- end
285
- end
286
- end
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2019, 2022, Optimizely and contributors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require_relative 'odp_event_api_manager'
19
+ require_relative '../helpers/constants'
20
+ require_relative 'odp_event'
21
+
22
+ module Optimizely
23
+ class OdpEventManager
24
+ # Events passed to the OdpEventManager are immediately added to an EventQueue.
25
+ # The OdpEventManager maintains a single consumer thread that pulls events off of
26
+ # the BlockingQueue and buffers them for either a configured batch size or for a
27
+ # maximum duration before the resulting OdpEvent is sent to Odp.
28
+
29
+ attr_reader :batch_size, :api_manager, :logger
30
+ attr_accessor :odp_config
31
+
32
+ def initialize(
33
+ api_manager: nil,
34
+ logger: NoOpLogger.new,
35
+ proxy_config: nil,
36
+ request_timeout: nil,
37
+ flush_interval: nil
38
+ )
39
+ @odp_config = nil
40
+ @api_host = nil
41
+ @api_key = nil
42
+
43
+ @mutex = Mutex.new
44
+ @event_queue = SizedQueue.new(Optimizely::Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_QUEUE_CAPACITY])
45
+ @queue_capacity = Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_QUEUE_CAPACITY]
46
+ # received signal should be sent after adding item to event_queue
47
+ @received = ConditionVariable.new
48
+ @logger = logger
49
+ @api_manager = api_manager || OdpEventApiManager.new(logger: @logger, proxy_config: proxy_config, timeout: request_timeout)
50
+ @flush_interval = flush_interval || Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_FLUSH_INTERVAL_SECONDS]
51
+ @batch_size = @flush_interval&.zero? ? 1 : Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_BATCH_SIZE]
52
+ @flush_deadline = 0
53
+ @retry_count = Helpers::Constants::ODP_EVENT_MANAGER[:DEFAULT_RETRY_COUNT]
54
+ # current_batch should only be accessed by processing thread
55
+ @current_batch = []
56
+ @thread = nil
57
+ @thread_exception = false
58
+ end
59
+
60
+ def start!(odp_config)
61
+ if running?
62
+ @logger.log(Logger::WARN, 'Service already started.')
63
+ return
64
+ end
65
+
66
+ @odp_config = odp_config
67
+ @api_host = odp_config.api_host
68
+ @api_key = odp_config.api_key
69
+
70
+ @thread = Thread.new { run }
71
+ @logger.log(Logger::INFO, 'Starting scheduler.')
72
+ end
73
+
74
+ def flush
75
+ begin
76
+ @event_queue.push(:FLUSH_SIGNAL, true)
77
+ rescue ThreadError
78
+ @logger.log(Logger::ERROR, 'Error flushing ODP event queue.')
79
+ return
80
+ end
81
+
82
+ @mutex.synchronize do
83
+ @received.signal
84
+ end
85
+ end
86
+
87
+ def update_config
88
+ begin
89
+ # Adds update config signal to event_queue.
90
+ @event_queue.push(:UPDATE_CONFIG, true)
91
+ rescue ThreadError
92
+ @logger.log(Logger::ERROR, 'Error updating ODP config for the event queue')
93
+ end
94
+
95
+ @mutex.synchronize do
96
+ @received.signal
97
+ end
98
+ end
99
+
100
+ def dispatch(event)
101
+ if @thread_exception
102
+ @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], 'Queue is down'))
103
+ return
104
+ end
105
+
106
+ # if the processor has been explicitly stopped. Don't accept tasks
107
+ unless running?
108
+ @logger.log(Logger::WARN, 'ODP event queue is shutdown, not accepting events.')
109
+ return
110
+ end
111
+
112
+ begin
113
+ @logger.log(Logger::DEBUG, 'ODP event queue: adding event.')
114
+ @event_queue.push(event, true)
115
+ rescue => e
116
+ @logger.log(Logger::WARN, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], e.message))
117
+ return
118
+ end
119
+
120
+ @mutex.synchronize do
121
+ @received.signal
122
+ end
123
+ end
124
+
125
+ def send_event(type:, action:, identifiers:, data:)
126
+ case @odp_config&.odp_state
127
+ when nil
128
+ @logger.log(Logger::DEBUG, 'ODP event queue: cannot send before config has been set.')
129
+ return
130
+ when OdpConfig::ODP_CONFIG_STATE[:UNDETERMINED]
131
+ @logger.log(Logger::DEBUG, 'ODP event queue: cannot send before the datafile has loaded.')
132
+ return
133
+ when OdpConfig::ODP_CONFIG_STATE[:NOT_INTEGRATED]
134
+ @logger.log(Logger::DEBUG, Helpers::Constants::ODP_LOGS[:ODP_NOT_INTEGRATED])
135
+ return
136
+ end
137
+
138
+ event = Optimizely::OdpEvent.new(type: type, action: action, identifiers: identifiers, data: data)
139
+ dispatch(event)
140
+ end
141
+
142
+ def stop!
143
+ return unless running?
144
+
145
+ begin
146
+ @event_queue.push(:SHUTDOWN_SIGNAL, true)
147
+ rescue ThreadError
148
+ @logger.log(Logger::ERROR, 'Error stopping ODP event queue.')
149
+ return
150
+ end
151
+
152
+ @event_queue.close
153
+
154
+ @mutex.synchronize do
155
+ @received.signal
156
+ end
157
+
158
+ @logger.log(Logger::INFO, 'Stopping ODP event queue.')
159
+
160
+ @thread.join
161
+
162
+ @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], @current_batch.to_json)) unless @current_batch.empty?
163
+ end
164
+
165
+ def running?
166
+ !!@thread && !!@thread.status && !@event_queue.closed?
167
+ end
168
+
169
+ private
170
+
171
+ def run
172
+ loop do
173
+ @mutex.synchronize do
174
+ @received.wait(@mutex, queue_timeout) if @event_queue.empty?
175
+ end
176
+
177
+ begin
178
+ item = @event_queue.pop(true)
179
+ rescue ThreadError => e
180
+ raise unless e.message == 'queue empty'
181
+
182
+ item = nil
183
+ end
184
+
185
+ case item
186
+ when :SHUTDOWN_SIGNAL
187
+ @logger.log(Logger::DEBUG, 'ODP event queue: received shutdown signal.')
188
+ break
189
+
190
+ when :FLUSH_SIGNAL
191
+ @logger.log(Logger::DEBUG, 'ODP event queue: received flush signal.')
192
+ flush_batch!
193
+
194
+ when :UPDATE_CONFIG
195
+ @logger.log(Logger::DEBUG, 'ODP event queue: received update config signal.')
196
+ process_config_update
197
+
198
+ when Optimizely::OdpEvent
199
+ add_to_batch(item)
200
+
201
+ when nil && !@current_batch.empty?
202
+ @logger.log(Logger::DEBUG, 'ODP event queue: flushing on interval.')
203
+ flush_batch!
204
+ end
205
+ end
206
+ rescue SignalException
207
+ @thread_exception = true
208
+ @logger.log(Logger::ERROR, 'Interrupted while processing ODP events.')
209
+ rescue => e
210
+ @thread_exception = true
211
+ @logger.log(Logger::ERROR, "Uncaught exception processing ODP events. Error: #{e.message}")
212
+ ensure
213
+ @logger.log(Logger::INFO, 'Exiting ODP processing loop. Attempting to flush pending events.')
214
+ flush_batch!
215
+ end
216
+
217
+ def flush_batch!
218
+ if @current_batch.empty?
219
+ @logger.log(Logger::DEBUG, 'ODP event queue: nothing to flush.')
220
+ return
221
+ end
222
+
223
+ if @api_key.nil? || @api_host.nil?
224
+ @logger.log(Logger::DEBUG, Helpers::Constants::ODP_LOGS[:ODP_NOT_INTEGRATED])
225
+ @current_batch.clear
226
+ return
227
+ end
228
+
229
+ @logger.log(Logger::DEBUG, "ODP event queue: flushing batch size #{@current_batch.length}.")
230
+ should_retry = false
231
+
232
+ i = 0
233
+ while i < @retry_count
234
+ begin
235
+ should_retry = @api_manager.send_odp_events(@api_key, @api_host, @current_batch)
236
+ rescue StandardError => e
237
+ should_retry = false
238
+ @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], "Error: #{e.message} #{@current_batch.to_json}"))
239
+ end
240
+ break unless should_retry
241
+
242
+ @logger.log(Logger::DEBUG, 'Error dispatching ODP events, scheduled to retry.') if i < @retry_count
243
+ i += 1
244
+ end
245
+
246
+ @logger.log(Logger::ERROR, format(Helpers::Constants::ODP_LOGS[:ODP_EVENT_FAILED], "Failed after #{i} retries: #{@current_batch.to_json}")) if should_retry
247
+
248
+ @current_batch.clear
249
+ end
250
+
251
+ def add_to_batch(event)
252
+ set_flush_deadline if @current_batch.empty?
253
+
254
+ @current_batch << event
255
+ return unless @current_batch.length >= @batch_size
256
+
257
+ @logger.log(Logger::DEBUG, 'ODP event queue: flushing on batch size.')
258
+ flush_batch!
259
+ end
260
+
261
+ def set_flush_deadline
262
+ # Sets time that next flush will occur.
263
+ @flush_deadline = Time.new + @flush_interval
264
+ end
265
+
266
+ def time_till_flush
267
+ # Returns seconds until next flush; no less than 0.
268
+ [0, @flush_deadline - Time.new].max
269
+ end
270
+
271
+ def queue_timeout
272
+ # Returns seconds until next flush or None if current batch is empty.
273
+ return nil if @current_batch.empty?
274
+
275
+ time_till_flush
276
+ end
277
+
278
+ def process_config_update
279
+ # Updates the configuration used to send events.
280
+ flush_batch! unless @current_batch.empty?
281
+
282
+ @api_key = @odp_config&.api_key
283
+ @api_host = @odp_config&.api_host
284
+ end
285
+ end
286
+ end