optimizely-sdk 5.0.0 → 5.0.1
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.
- checksums.yaml +4 -4
- data/LICENSE +202 -202
- data/lib/optimizely/audience.rb +127 -127
- data/lib/optimizely/bucketer.rb +156 -156
- data/lib/optimizely/condition_tree_evaluator.rb +123 -123
- data/lib/optimizely/config/datafile_project_config.rb +558 -558
- data/lib/optimizely/config/proxy_config.rb +34 -34
- data/lib/optimizely/config_manager/async_scheduler.rb +95 -95
- data/lib/optimizely/config_manager/http_project_config_manager.rb +340 -340
- data/lib/optimizely/config_manager/project_config_manager.rb +25 -25
- data/lib/optimizely/config_manager/static_project_config_manager.rb +55 -55
- data/lib/optimizely/decide/optimizely_decide_option.rb +28 -28
- data/lib/optimizely/decide/optimizely_decision.rb +60 -60
- data/lib/optimizely/decide/optimizely_decision_message.rb +26 -26
- data/lib/optimizely/decision_service.rb +563 -563
- data/lib/optimizely/error_handler.rb +39 -39
- data/lib/optimizely/event/batch_event_processor.rb +235 -235
- data/lib/optimizely/event/entity/conversion_event.rb +44 -44
- data/lib/optimizely/event/entity/decision.rb +38 -38
- data/lib/optimizely/event/entity/event_batch.rb +86 -86
- data/lib/optimizely/event/entity/event_context.rb +50 -50
- data/lib/optimizely/event/entity/impression_event.rb +48 -48
- data/lib/optimizely/event/entity/snapshot.rb +33 -33
- data/lib/optimizely/event/entity/snapshot_event.rb +48 -48
- data/lib/optimizely/event/entity/user_event.rb +22 -22
- data/lib/optimizely/event/entity/visitor.rb +36 -36
- data/lib/optimizely/event/entity/visitor_attribute.rb +38 -38
- data/lib/optimizely/event/event_factory.rb +156 -156
- data/lib/optimizely/event/event_processor.rb +25 -25
- data/lib/optimizely/event/forwarding_event_processor.rb +44 -44
- data/lib/optimizely/event/user_event_factory.rb +88 -88
- data/lib/optimizely/event_builder.rb +221 -221
- data/lib/optimizely/event_dispatcher.rb +69 -69
- data/lib/optimizely/exceptions.rb +193 -193
- data/lib/optimizely/helpers/constants.rb +459 -459
- data/lib/optimizely/helpers/date_time_utils.rb +30 -30
- data/lib/optimizely/helpers/event_tag_utils.rb +132 -132
- data/lib/optimizely/helpers/group.rb +31 -31
- data/lib/optimizely/helpers/http_utils.rb +68 -68
- data/lib/optimizely/helpers/sdk_settings.rb +61 -61
- data/lib/optimizely/helpers/validator.rb +236 -236
- data/lib/optimizely/helpers/variable_type.rb +67 -67
- data/lib/optimizely/logger.rb +46 -46
- data/lib/optimizely/notification_center.rb +174 -174
- data/lib/optimizely/notification_center_registry.rb +71 -71
- data/lib/optimizely/odp/lru_cache.rb +114 -114
- data/lib/optimizely/odp/odp_config.rb +102 -102
- data/lib/optimizely/odp/odp_event.rb +75 -75
- data/lib/optimizely/odp/odp_event_api_manager.rb +70 -70
- data/lib/optimizely/odp/odp_event_manager.rb +286 -286
- data/lib/optimizely/odp/odp_manager.rb +159 -159
- data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -122
- data/lib/optimizely/odp/odp_segment_manager.rb +97 -97
- data/lib/optimizely/optimizely_config.rb +273 -273
- data/lib/optimizely/optimizely_factory.rb +184 -184
- data/lib/optimizely/optimizely_user_context.rb +238 -238
- data/lib/optimizely/params.rb +31 -31
- data/lib/optimizely/project_config.rb +99 -99
- data/lib/optimizely/semantic_version.rb +166 -166
- data/lib/optimizely/user_condition_evaluator.rb +391 -391
- data/lib/optimizely/user_profile_service.rb +35 -35
- data/lib/optimizely/version.rb +21 -21
- data/lib/optimizely.rb +1262 -1262
- metadata +7 -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
|