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,39 +1,39 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
#
|
4
|
-
# Copyright 2016-2017, 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
|
-
module Optimizely
|
19
|
-
class BaseErrorHandler
|
20
|
-
# Class encapsulating exception handling functionality.
|
21
|
-
# Override with your own exception handler providing a handle_error method.
|
22
|
-
|
23
|
-
def handle_error(_error); end
|
24
|
-
end
|
25
|
-
|
26
|
-
class NoOpErrorHandler < BaseErrorHandler
|
27
|
-
# Class providing handle_error method that suppresses errors.
|
28
|
-
|
29
|
-
def handle_error(_error); end
|
30
|
-
end
|
31
|
-
|
32
|
-
class RaiseErrorHandler < BaseErrorHandler
|
33
|
-
# Class providing a handle_error method that raises exceptions.
|
34
|
-
|
35
|
-
def handle_error(error)
|
36
|
-
raise error
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright 2016-2017, 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
|
+
module Optimizely
|
19
|
+
class BaseErrorHandler
|
20
|
+
# Class encapsulating exception handling functionality.
|
21
|
+
# Override with your own exception handler providing a handle_error method.
|
22
|
+
|
23
|
+
def handle_error(_error); end
|
24
|
+
end
|
25
|
+
|
26
|
+
class NoOpErrorHandler < BaseErrorHandler
|
27
|
+
# Class providing handle_error method that suppresses errors.
|
28
|
+
|
29
|
+
def handle_error(_error); end
|
30
|
+
end
|
31
|
+
|
32
|
+
class RaiseErrorHandler < BaseErrorHandler
|
33
|
+
# Class providing a handle_error method that raises exceptions.
|
34
|
+
|
35
|
+
def handle_error(error)
|
36
|
+
raise error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,235 +1,235 @@
|
|
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 'event_processor'
|
19
|
-
require_relative '../helpers/validator'
|
20
|
-
module Optimizely
|
21
|
-
class BatchEventProcessor < EventProcessor
|
22
|
-
# BatchEventProcessor is a batched implementation of the Interface EventProcessor.
|
23
|
-
# Events passed to the BatchEventProcessor are immediately added to an EventQueue.
|
24
|
-
# The BatchEventProcessor maintains a single consumer thread that pulls events off of
|
25
|
-
# the BlockingQueue and buffers them for either a configured batch size or for a
|
26
|
-
# maximum duration before the resulting LogEvent is sent to the NotificationCenter.
|
27
|
-
|
28
|
-
attr_reader :event_queue, :event_dispatcher, :current_batch, :started, :batch_size, :flush_interval
|
29
|
-
|
30
|
-
DEFAULT_BATCH_SIZE = 10
|
31
|
-
DEFAULT_BATCH_INTERVAL = 30_000 # interval in milliseconds
|
32
|
-
DEFAULT_QUEUE_CAPACITY = 1000
|
33
|
-
DEFAULT_TIMEOUT_INTERVAL = 5 # interval in seconds
|
34
|
-
|
35
|
-
FLUSH_SIGNAL = 'FLUSH_SIGNAL'
|
36
|
-
SHUTDOWN_SIGNAL = 'SHUTDOWN_SIGNAL'
|
37
|
-
|
38
|
-
def initialize(
|
39
|
-
event_queue: SizedQueue.new(DEFAULT_QUEUE_CAPACITY),
|
40
|
-
event_dispatcher: nil,
|
41
|
-
batch_size: DEFAULT_BATCH_SIZE,
|
42
|
-
flush_interval: DEFAULT_BATCH_INTERVAL,
|
43
|
-
logger: NoOpLogger.new,
|
44
|
-
notification_center: nil
|
45
|
-
)
|
46
|
-
super()
|
47
|
-
@event_queue = event_queue
|
48
|
-
@logger = logger
|
49
|
-
@event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger)
|
50
|
-
@batch_size = if (batch_size.is_a? Integer) && positive_number?(batch_size)
|
51
|
-
batch_size
|
52
|
-
else
|
53
|
-
@logger.log(Logger::DEBUG, "Setting to default batch_size: #{DEFAULT_BATCH_SIZE}.")
|
54
|
-
DEFAULT_BATCH_SIZE
|
55
|
-
end
|
56
|
-
@flush_interval = if positive_number?(flush_interval)
|
57
|
-
flush_interval
|
58
|
-
else
|
59
|
-
@logger.log(Logger::DEBUG, "Setting to default flush_interval: #{DEFAULT_BATCH_INTERVAL} ms.")
|
60
|
-
DEFAULT_BATCH_INTERVAL
|
61
|
-
end
|
62
|
-
@notification_center = notification_center
|
63
|
-
@current_batch = []
|
64
|
-
@started = false
|
65
|
-
@stopped = false
|
66
|
-
end
|
67
|
-
|
68
|
-
def start!
|
69
|
-
if @started == true
|
70
|
-
@logger.log(Logger::WARN, 'Service already started.')
|
71
|
-
return
|
72
|
-
end
|
73
|
-
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
74
|
-
@logger.log(Logger::INFO, 'Starting scheduler.')
|
75
|
-
if @wait_mutex.nil?
|
76
|
-
@wait_mutex = Mutex.new
|
77
|
-
@resource = ConditionVariable.new
|
78
|
-
end
|
79
|
-
@thread = Thread.new { run_queue }
|
80
|
-
@started = true
|
81
|
-
@stopped = false
|
82
|
-
end
|
83
|
-
|
84
|
-
def flush
|
85
|
-
@event_queue << FLUSH_SIGNAL
|
86
|
-
@wait_mutex.synchronize { @resource.signal }
|
87
|
-
end
|
88
|
-
|
89
|
-
def process(user_event)
|
90
|
-
@logger.log(Logger::DEBUG, "Received userEvent: #{user_event}")
|
91
|
-
|
92
|
-
# if the processor has been explicitly stopped. Don't accept tasks
|
93
|
-
if @stopped
|
94
|
-
@logger.log(Logger::WARN, 'Executor shutdown, not accepting tasks.')
|
95
|
-
return
|
96
|
-
end
|
97
|
-
|
98
|
-
# start if the processor hasn't been started
|
99
|
-
start! unless @started
|
100
|
-
|
101
|
-
begin
|
102
|
-
@event_queue.push(user_event, true)
|
103
|
-
@wait_mutex.synchronize { @resource.signal }
|
104
|
-
rescue => e
|
105
|
-
@logger.log(Logger::WARN, "Payload not accepted by the queue: #{e.message}")
|
106
|
-
nil
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def stop!
|
111
|
-
return unless @started
|
112
|
-
|
113
|
-
@logger.log(Logger::INFO, 'Stopping scheduler.')
|
114
|
-
@event_queue << SHUTDOWN_SIGNAL
|
115
|
-
@wait_mutex.synchronize { @resource.signal }
|
116
|
-
@thread.join(DEFAULT_TIMEOUT_INTERVAL)
|
117
|
-
@started = false
|
118
|
-
@stopped = true
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def process_queue
|
124
|
-
while @event_queue.length.positive?
|
125
|
-
item = @event_queue.pop
|
126
|
-
if item == SHUTDOWN_SIGNAL
|
127
|
-
@logger.log(Logger::DEBUG, 'Received shutdown signal.')
|
128
|
-
return false
|
129
|
-
end
|
130
|
-
|
131
|
-
if item == FLUSH_SIGNAL
|
132
|
-
@logger.log(Logger::DEBUG, 'Received flush signal.')
|
133
|
-
flush_queue!
|
134
|
-
next
|
135
|
-
end
|
136
|
-
|
137
|
-
add_to_batch(item) if item.is_a? Optimizely::UserEvent
|
138
|
-
end
|
139
|
-
true
|
140
|
-
end
|
141
|
-
|
142
|
-
def run_queue
|
143
|
-
loop do
|
144
|
-
if Helpers::DateTimeUtils.create_timestamp >= @flushing_interval_deadline
|
145
|
-
@logger.log(Logger::DEBUG, 'Deadline exceeded flushing current batch.')
|
146
|
-
|
147
|
-
break unless process_queue
|
148
|
-
|
149
|
-
flush_queue!
|
150
|
-
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
151
|
-
end
|
152
|
-
|
153
|
-
break unless process_queue
|
154
|
-
|
155
|
-
# what is the current interval to flush in seconds
|
156
|
-
interval = (@flushing_interval_deadline - Helpers::DateTimeUtils.create_timestamp) * 0.001
|
157
|
-
|
158
|
-
next unless interval.positive?
|
159
|
-
|
160
|
-
@wait_mutex.synchronize { @resource.wait(@wait_mutex, interval) }
|
161
|
-
end
|
162
|
-
rescue SignalException
|
163
|
-
@logger.log(Logger::ERROR, 'Interrupted while processing buffer.')
|
164
|
-
rescue => e
|
165
|
-
@logger.log(Logger::ERROR, "Uncaught exception processing buffer. #{e.message}")
|
166
|
-
ensure
|
167
|
-
@logger.log(Logger::INFO, 'Exiting processing loop. Attempting to flush pending events.')
|
168
|
-
flush_queue!
|
169
|
-
end
|
170
|
-
|
171
|
-
def flush_queue!
|
172
|
-
return if @current_batch.empty?
|
173
|
-
|
174
|
-
log_event = Optimizely::EventFactory.create_log_event(@current_batch, @logger)
|
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}.")
|
188
|
-
end
|
189
|
-
@current_batch = []
|
190
|
-
end
|
191
|
-
|
192
|
-
def add_to_batch(user_event)
|
193
|
-
if should_split?(user_event)
|
194
|
-
flush_queue!
|
195
|
-
@current_batch = []
|
196
|
-
end
|
197
|
-
|
198
|
-
# Reset the deadline if starting a new batch.
|
199
|
-
@flushing_interval_deadline = (Helpers::DateTimeUtils.create_timestamp + @flush_interval) if @current_batch.empty?
|
200
|
-
|
201
|
-
@logger.log(Logger::DEBUG, "Adding user event: #{user_event} to batch.")
|
202
|
-
@current_batch << user_event
|
203
|
-
return unless @current_batch.length >= @batch_size
|
204
|
-
|
205
|
-
@logger.log(Logger::DEBUG, 'Flushing on max batch size.')
|
206
|
-
flush_queue!
|
207
|
-
end
|
208
|
-
|
209
|
-
def should_split?(user_event)
|
210
|
-
return false if @current_batch.empty?
|
211
|
-
|
212
|
-
current_context = @current_batch.last.event_context
|
213
|
-
new_context = user_event.event_context
|
214
|
-
|
215
|
-
# Revisions should match
|
216
|
-
unless current_context[:revision] == new_context[:revision]
|
217
|
-
@logger.log(Logger::DEBUG, 'Revisions mismatched: Flushing current batch.')
|
218
|
-
return true
|
219
|
-
end
|
220
|
-
|
221
|
-
# Projects should match
|
222
|
-
unless current_context[:project_id] == new_context[:project_id]
|
223
|
-
@logger.log(Logger::DEBUG, 'Project Ids mismatched: Flushing current batch.')
|
224
|
-
return true
|
225
|
-
end
|
226
|
-
false
|
227
|
-
end
|
228
|
-
|
229
|
-
def positive_number?(value)
|
230
|
-
# Returns true if the given value is positive finite number.
|
231
|
-
# false otherwise.
|
232
|
-
Helpers::Validator.finite_number?(value) && value.positive?
|
233
|
-
end
|
234
|
-
end
|
235
|
-
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 'event_processor'
|
19
|
+
require_relative '../helpers/validator'
|
20
|
+
module Optimizely
|
21
|
+
class BatchEventProcessor < EventProcessor
|
22
|
+
# BatchEventProcessor is a batched implementation of the Interface EventProcessor.
|
23
|
+
# Events passed to the BatchEventProcessor are immediately added to an EventQueue.
|
24
|
+
# The BatchEventProcessor maintains a single consumer thread that pulls events off of
|
25
|
+
# the BlockingQueue and buffers them for either a configured batch size or for a
|
26
|
+
# maximum duration before the resulting LogEvent is sent to the NotificationCenter.
|
27
|
+
|
28
|
+
attr_reader :event_queue, :event_dispatcher, :current_batch, :started, :batch_size, :flush_interval
|
29
|
+
|
30
|
+
DEFAULT_BATCH_SIZE = 10
|
31
|
+
DEFAULT_BATCH_INTERVAL = 30_000 # interval in milliseconds
|
32
|
+
DEFAULT_QUEUE_CAPACITY = 1000
|
33
|
+
DEFAULT_TIMEOUT_INTERVAL = 5 # interval in seconds
|
34
|
+
|
35
|
+
FLUSH_SIGNAL = 'FLUSH_SIGNAL'
|
36
|
+
SHUTDOWN_SIGNAL = 'SHUTDOWN_SIGNAL'
|
37
|
+
|
38
|
+
def initialize(
|
39
|
+
event_queue: SizedQueue.new(DEFAULT_QUEUE_CAPACITY),
|
40
|
+
event_dispatcher: nil,
|
41
|
+
batch_size: DEFAULT_BATCH_SIZE,
|
42
|
+
flush_interval: DEFAULT_BATCH_INTERVAL,
|
43
|
+
logger: NoOpLogger.new,
|
44
|
+
notification_center: nil
|
45
|
+
)
|
46
|
+
super()
|
47
|
+
@event_queue = event_queue
|
48
|
+
@logger = logger
|
49
|
+
@event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger)
|
50
|
+
@batch_size = if (batch_size.is_a? Integer) && positive_number?(batch_size)
|
51
|
+
batch_size
|
52
|
+
else
|
53
|
+
@logger.log(Logger::DEBUG, "Setting to default batch_size: #{DEFAULT_BATCH_SIZE}.")
|
54
|
+
DEFAULT_BATCH_SIZE
|
55
|
+
end
|
56
|
+
@flush_interval = if positive_number?(flush_interval)
|
57
|
+
flush_interval
|
58
|
+
else
|
59
|
+
@logger.log(Logger::DEBUG, "Setting to default flush_interval: #{DEFAULT_BATCH_INTERVAL} ms.")
|
60
|
+
DEFAULT_BATCH_INTERVAL
|
61
|
+
end
|
62
|
+
@notification_center = notification_center
|
63
|
+
@current_batch = []
|
64
|
+
@started = false
|
65
|
+
@stopped = false
|
66
|
+
end
|
67
|
+
|
68
|
+
def start!
|
69
|
+
if @started == true
|
70
|
+
@logger.log(Logger::WARN, 'Service already started.')
|
71
|
+
return
|
72
|
+
end
|
73
|
+
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
74
|
+
@logger.log(Logger::INFO, 'Starting scheduler.')
|
75
|
+
if @wait_mutex.nil?
|
76
|
+
@wait_mutex = Mutex.new
|
77
|
+
@resource = ConditionVariable.new
|
78
|
+
end
|
79
|
+
@thread = Thread.new { run_queue }
|
80
|
+
@started = true
|
81
|
+
@stopped = false
|
82
|
+
end
|
83
|
+
|
84
|
+
def flush
|
85
|
+
@event_queue << FLUSH_SIGNAL
|
86
|
+
@wait_mutex.synchronize { @resource.signal }
|
87
|
+
end
|
88
|
+
|
89
|
+
def process(user_event)
|
90
|
+
@logger.log(Logger::DEBUG, "Received userEvent: #{user_event}")
|
91
|
+
|
92
|
+
# if the processor has been explicitly stopped. Don't accept tasks
|
93
|
+
if @stopped
|
94
|
+
@logger.log(Logger::WARN, 'Executor shutdown, not accepting tasks.')
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
# start if the processor hasn't been started
|
99
|
+
start! unless @started
|
100
|
+
|
101
|
+
begin
|
102
|
+
@event_queue.push(user_event, true)
|
103
|
+
@wait_mutex.synchronize { @resource.signal }
|
104
|
+
rescue => e
|
105
|
+
@logger.log(Logger::WARN, "Payload not accepted by the queue: #{e.message}")
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def stop!
|
111
|
+
return unless @started
|
112
|
+
|
113
|
+
@logger.log(Logger::INFO, 'Stopping scheduler.')
|
114
|
+
@event_queue << SHUTDOWN_SIGNAL
|
115
|
+
@wait_mutex.synchronize { @resource.signal }
|
116
|
+
@thread.join(DEFAULT_TIMEOUT_INTERVAL)
|
117
|
+
@started = false
|
118
|
+
@stopped = true
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def process_queue
|
124
|
+
while @event_queue.length.positive?
|
125
|
+
item = @event_queue.pop
|
126
|
+
if item == SHUTDOWN_SIGNAL
|
127
|
+
@logger.log(Logger::DEBUG, 'Received shutdown signal.')
|
128
|
+
return false
|
129
|
+
end
|
130
|
+
|
131
|
+
if item == FLUSH_SIGNAL
|
132
|
+
@logger.log(Logger::DEBUG, 'Received flush signal.')
|
133
|
+
flush_queue!
|
134
|
+
next
|
135
|
+
end
|
136
|
+
|
137
|
+
add_to_batch(item) if item.is_a? Optimizely::UserEvent
|
138
|
+
end
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
def run_queue
|
143
|
+
loop do
|
144
|
+
if Helpers::DateTimeUtils.create_timestamp >= @flushing_interval_deadline
|
145
|
+
@logger.log(Logger::DEBUG, 'Deadline exceeded flushing current batch.')
|
146
|
+
|
147
|
+
break unless process_queue
|
148
|
+
|
149
|
+
flush_queue!
|
150
|
+
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
151
|
+
end
|
152
|
+
|
153
|
+
break unless process_queue
|
154
|
+
|
155
|
+
# what is the current interval to flush in seconds
|
156
|
+
interval = (@flushing_interval_deadline - Helpers::DateTimeUtils.create_timestamp) * 0.001
|
157
|
+
|
158
|
+
next unless interval.positive?
|
159
|
+
|
160
|
+
@wait_mutex.synchronize { @resource.wait(@wait_mutex, interval) }
|
161
|
+
end
|
162
|
+
rescue SignalException
|
163
|
+
@logger.log(Logger::ERROR, 'Interrupted while processing buffer.')
|
164
|
+
rescue => e
|
165
|
+
@logger.log(Logger::ERROR, "Uncaught exception processing buffer. #{e.message}")
|
166
|
+
ensure
|
167
|
+
@logger.log(Logger::INFO, 'Exiting processing loop. Attempting to flush pending events.')
|
168
|
+
flush_queue!
|
169
|
+
end
|
170
|
+
|
171
|
+
def flush_queue!
|
172
|
+
return if @current_batch.empty?
|
173
|
+
|
174
|
+
log_event = Optimizely::EventFactory.create_log_event(@current_batch, @logger)
|
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}.")
|
188
|
+
end
|
189
|
+
@current_batch = []
|
190
|
+
end
|
191
|
+
|
192
|
+
def add_to_batch(user_event)
|
193
|
+
if should_split?(user_event)
|
194
|
+
flush_queue!
|
195
|
+
@current_batch = []
|
196
|
+
end
|
197
|
+
|
198
|
+
# Reset the deadline if starting a new batch.
|
199
|
+
@flushing_interval_deadline = (Helpers::DateTimeUtils.create_timestamp + @flush_interval) if @current_batch.empty?
|
200
|
+
|
201
|
+
@logger.log(Logger::DEBUG, "Adding user event: #{user_event} to batch.")
|
202
|
+
@current_batch << user_event
|
203
|
+
return unless @current_batch.length >= @batch_size
|
204
|
+
|
205
|
+
@logger.log(Logger::DEBUG, 'Flushing on max batch size.')
|
206
|
+
flush_queue!
|
207
|
+
end
|
208
|
+
|
209
|
+
def should_split?(user_event)
|
210
|
+
return false if @current_batch.empty?
|
211
|
+
|
212
|
+
current_context = @current_batch.last.event_context
|
213
|
+
new_context = user_event.event_context
|
214
|
+
|
215
|
+
# Revisions should match
|
216
|
+
unless current_context[:revision] == new_context[:revision]
|
217
|
+
@logger.log(Logger::DEBUG, 'Revisions mismatched: Flushing current batch.')
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
|
221
|
+
# Projects should match
|
222
|
+
unless current_context[:project_id] == new_context[:project_id]
|
223
|
+
@logger.log(Logger::DEBUG, 'Project Ids mismatched: Flushing current batch.')
|
224
|
+
return true
|
225
|
+
end
|
226
|
+
false
|
227
|
+
end
|
228
|
+
|
229
|
+
def positive_number?(value)
|
230
|
+
# Returns true if the given value is positive finite number.
|
231
|
+
# false otherwise.
|
232
|
+
Helpers::Validator.finite_number?(value) && value.positive?
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -1,44 +1,44 @@
|
|
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 'user_event'
|
19
|
-
require 'optimizely/helpers/date_time_utils'
|
20
|
-
module Optimizely
|
21
|
-
class ConversionEvent < UserEvent
|
22
|
-
# Represents conversion event
|
23
|
-
attr_reader :event, :user_id, :visitor_attributes, :tags, :bot_filtering
|
24
|
-
|
25
|
-
def initialize(
|
26
|
-
event_context:,
|
27
|
-
event:,
|
28
|
-
user_id:,
|
29
|
-
visitor_attributes:,
|
30
|
-
tags:,
|
31
|
-
bot_filtering:
|
32
|
-
)
|
33
|
-
super()
|
34
|
-
@event_context = event_context
|
35
|
-
@uuid = SecureRandom.uuid
|
36
|
-
@timestamp = Helpers::DateTimeUtils.create_timestamp
|
37
|
-
@event = event
|
38
|
-
@user_id = user_id
|
39
|
-
@visitor_attributes = visitor_attributes
|
40
|
-
@tags = tags
|
41
|
-
@bot_filtering = bot_filtering
|
42
|
-
end
|
43
|
-
end
|
44
|
-
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 'user_event'
|
19
|
+
require 'optimizely/helpers/date_time_utils'
|
20
|
+
module Optimizely
|
21
|
+
class ConversionEvent < UserEvent
|
22
|
+
# Represents conversion event
|
23
|
+
attr_reader :event, :user_id, :visitor_attributes, :tags, :bot_filtering
|
24
|
+
|
25
|
+
def initialize(
|
26
|
+
event_context:,
|
27
|
+
event:,
|
28
|
+
user_id:,
|
29
|
+
visitor_attributes:,
|
30
|
+
tags:,
|
31
|
+
bot_filtering:
|
32
|
+
)
|
33
|
+
super()
|
34
|
+
@event_context = event_context
|
35
|
+
@uuid = SecureRandom.uuid
|
36
|
+
@timestamp = Helpers::DateTimeUtils.create_timestamp
|
37
|
+
@event = event
|
38
|
+
@user_id = user_id
|
39
|
+
@visitor_attributes = visitor_attributes
|
40
|
+
@tags = tags
|
41
|
+
@bot_filtering = bot_filtering
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|