splitclient-rb 7.0.4.pre.rc3-java → 7.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -3
- data/CHANGES.txt +3 -0
- data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +17 -6
- data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +21 -11
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +1 -1
- data/lib/splitclient-rb/clients/split_client.rb +4 -7
- data/lib/splitclient-rb/constants.rb +10 -0
- data/lib/splitclient-rb/engine/api/splits.rb +0 -1
- data/lib/splitclient-rb/engine/auth_api_client.rb +78 -0
- data/lib/splitclient-rb/engine/push_manager.rb +53 -0
- data/lib/splitclient-rb/engine/sync_manager.rb +123 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +90 -0
- data/lib/splitclient-rb/exceptions.rb +8 -0
- data/lib/splitclient-rb/helpers/thread_helper.rb +25 -0
- data/lib/splitclient-rb/split_config.rb +42 -4
- data/lib/splitclient-rb/split_factory.rb +25 -15
- data/lib/splitclient-rb/sse/event_source/back_off.rb +25 -0
- data/lib/splitclient-rb/sse/event_source/client.rb +85 -60
- data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
- data/lib/splitclient-rb/sse/notification_manager_keeper.rb +71 -0
- data/lib/splitclient-rb/sse/notification_processor.rb +50 -0
- data/lib/splitclient-rb/sse/sse_handler.rb +47 -52
- data/lib/splitclient-rb/sse/workers/segments_worker.rb +21 -6
- data/lib/splitclient-rb/sse/workers/splits_worker.rb +24 -6
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +10 -3
- data/splitclient-rb.gemspec +1 -0
- metadata +28 -7
- data/lib/splitclient-rb/engine/parser/split_adapter.rb +0 -105
- data/lib/splitclient-rb/sse/event_source/status.rb +0 -13
- data/lib/splitclient-rb/sse/workers/control_worker.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17ccf70cef8223ac14aa96da12031c3205aa0f97
|
4
|
+
data.tar.gz: bd6561b5b2ffaec5d86054096ed8a39c4de0e9e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 630a6b415a70618e1fddafbcfac110c942249e69787c65418bf2d7f545b591809c235e002a68e682c37b27042af10d6d8cf920fe00858a79b22224c117ad7ae9
|
7
|
+
data.tar.gz: 86f6adf182a94bdd00e9459f3d46cae7d1943320851d0e9a2cc437e454bfb5259184af34b6f73861670888220299a8c4c0c8cebc7b33391155a2763d8c0182e9
|
data/.rubocop.yml
CHANGED
@@ -2,18 +2,25 @@ Documentation:
|
|
2
2
|
Enabled: false
|
3
3
|
|
4
4
|
Metrics/AbcSize:
|
5
|
-
Max:
|
5
|
+
Max: 26
|
6
6
|
|
7
7
|
Metrics/MethodLength:
|
8
8
|
Max: 20
|
9
9
|
|
10
10
|
Metrics/ClassLength:
|
11
|
-
Max:
|
11
|
+
Max: 150
|
12
12
|
|
13
13
|
Metrics/LineLength:
|
14
|
-
Max:
|
14
|
+
Max: 130
|
15
15
|
Exclude:
|
16
16
|
- spec/sse/**/*
|
17
|
+
- spec/integrations/**/*
|
18
|
+
- spec/engine/sync_manager_spec.rb
|
19
|
+
|
20
|
+
Style/BracesAroundHashParameters:
|
21
|
+
Exclude:
|
22
|
+
- spec/integrations/push_client_spec.rb
|
23
|
+
- spec/engine/synchronizer_spec.rb
|
17
24
|
|
18
25
|
Metrics/BlockLength:
|
19
26
|
Exclude:
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
7.1.0 (Apr 30, 2020)
|
2
|
+
- Added support for the new Split streaming architecture. When enabled, the SDK will not poll for updates but instead receive notifications every time there's a change in your environments, allowing to process those much quicker. If disabled (default) or in the event of an issue, the SDK will fallback to the known polling mechanism to provide a seamless experience.
|
3
|
+
|
1
4
|
7.0.3 (Jan 20, 2020)
|
2
5
|
- Added integration tests.
|
3
6
|
- Fixed impressions labels.
|
@@ -10,6 +10,7 @@ module SplitIoClient
|
|
10
10
|
@metrics = metrics
|
11
11
|
@config = config
|
12
12
|
@sdk_blocker = sdk_blocker
|
13
|
+
@semaphore = Mutex.new
|
13
14
|
end
|
14
15
|
|
15
16
|
def call
|
@@ -27,24 +28,34 @@ module SplitIoClient
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def fetch_segment(name)
|
30
|
-
|
31
|
+
@semaphore.synchronize do
|
32
|
+
segments_api.fetch_segments_by_names([name])
|
33
|
+
true
|
34
|
+
end
|
31
35
|
rescue StandardError => error
|
32
36
|
@config.log_found_exception(__method__.to_s, error)
|
37
|
+
false
|
33
38
|
end
|
34
39
|
|
35
40
|
def fetch_segments
|
36
|
-
|
41
|
+
@semaphore.synchronize do
|
42
|
+
segments_api.fetch_segments_by_names(@segments_repository.used_segment_names)
|
37
43
|
|
38
|
-
|
44
|
+
@sdk_blocker.segments_ready!
|
45
|
+
end
|
39
46
|
rescue StandardError => error
|
40
47
|
@config.log_found_exception(__method__.to_s, error)
|
41
48
|
end
|
42
49
|
|
50
|
+
def stop_segments_thread
|
51
|
+
SplitIoClient::Helpers::ThreadHelper.stop(:segment_fetcher, @config)
|
52
|
+
end
|
53
|
+
|
43
54
|
private
|
44
55
|
|
45
56
|
def segments_thread
|
46
57
|
@config.threads[:segment_fetcher] = Thread.new do
|
47
|
-
@config.logger.info('Starting segments fetcher service')
|
58
|
+
@config.logger.info('Starting segments fetcher service') if @config.debug_enabled
|
48
59
|
|
49
60
|
loop do
|
50
61
|
next unless @sdk_blocker.splits_repository.ready?
|
@@ -52,8 +63,8 @@ module SplitIoClient
|
|
52
63
|
fetch_segments
|
53
64
|
@config.logger.debug("Segment names: #{@segments_repository.used_segment_names.to_a}") if @config.debug_enabled
|
54
65
|
|
55
|
-
sleep_for = StoreUtils.random_interval(@config.segments_refresh_rate)
|
56
|
-
@config.logger.debug("Segments
|
66
|
+
sleep_for = SplitIoClient::Cache::Stores::StoreUtils.random_interval(@config.segments_refresh_rate)
|
67
|
+
@config.logger.debug("Segments fetcher is sleeping for: #{sleep_for} seconds") if @config.debug_enabled
|
57
68
|
sleep(sleep_for)
|
58
69
|
end
|
59
70
|
end
|
@@ -10,6 +10,7 @@ module SplitIoClient
|
|
10
10
|
@metrics = metrics
|
11
11
|
@config = config
|
12
12
|
@sdk_blocker = sdk_blocker
|
13
|
+
@semaphore = Mutex.new
|
13
14
|
end
|
14
15
|
|
15
16
|
def call
|
@@ -27,32 +28,41 @@ module SplitIoClient
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def fetch_splits
|
30
|
-
|
31
|
+
@semaphore.synchronize do
|
32
|
+
data = splits_since(@splits_repository.get_change_number)
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@splits_repository.set_segment_names(data[:segment_names])
|
37
|
-
@splits_repository.set_change_number(data[:till])
|
34
|
+
data[:splits] && data[:splits].each do |split|
|
35
|
+
add_split_unless_archived(split)
|
36
|
+
end
|
38
37
|
|
39
|
-
|
38
|
+
@splits_repository.set_segment_names(data[:segment_names])
|
39
|
+
@splits_repository.set_change_number(data[:till])
|
40
40
|
|
41
|
-
|
41
|
+
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
|
42
42
|
|
43
|
+
@sdk_blocker.splits_ready!
|
44
|
+
true
|
45
|
+
end
|
43
46
|
rescue StandardError => error
|
44
47
|
@config.log_found_exception(__method__.to_s, error)
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def stop_splits_thread
|
52
|
+
SplitIoClient::Helpers::ThreadHelper.stop(:split_fetcher, @config)
|
45
53
|
end
|
46
54
|
|
47
55
|
private
|
48
56
|
|
49
57
|
def splits_thread
|
50
58
|
@config.threads[:split_fetcher] = Thread.new do
|
51
|
-
@config.logger.info('Starting splits fetcher service')
|
59
|
+
@config.logger.info('Starting splits fetcher service') if @config.debug_enabled
|
52
60
|
loop do
|
53
61
|
fetch_splits
|
54
62
|
|
55
|
-
|
63
|
+
sleep_for = SplitIoClient::Cache::Stores::StoreUtils.random_interval(@config.features_refresh_rate)
|
64
|
+
@config.logger.debug("Splits fetcher is sleeping for: #{sleep_for} seconds") if @config.debug_enabled
|
65
|
+
sleep(sleep_for)
|
56
66
|
end
|
57
67
|
end
|
58
68
|
end
|
@@ -9,8 +9,9 @@ module SplitIoClient
|
|
9
9
|
# @param api_key [String] the API key for your split account
|
10
10
|
#
|
11
11
|
# @return [SplitIoClient] split.io client instance
|
12
|
-
def initialize(api_key,
|
12
|
+
def initialize(api_key, metrics, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker, config)
|
13
13
|
@api_key = api_key
|
14
|
+
@metrics = metrics
|
14
15
|
@splits_repository = splits_repository
|
15
16
|
@segments_repository = segments_repository
|
16
17
|
@impressions_repository = impressions_repository
|
@@ -19,8 +20,6 @@ module SplitIoClient
|
|
19
20
|
@sdk_blocker = sdk_blocker
|
20
21
|
@destroyed = false
|
21
22
|
@config = config
|
22
|
-
@adapter = adapter
|
23
|
-
@sse_handler = sse_handler
|
24
23
|
end
|
25
24
|
|
26
25
|
def get_treatment(
|
@@ -62,8 +61,6 @@ module SplitIoClient
|
|
62
61
|
thread.join
|
63
62
|
end
|
64
63
|
|
65
|
-
@sse_handler.sse_client.close if @config.push_notification_enabled
|
66
|
-
|
67
64
|
@config.threads.values.each { |thread| Thread.kill(thread) }
|
68
65
|
|
69
66
|
@splits_repository.clear
|
@@ -252,7 +249,7 @@ module SplitIoClient
|
|
252
249
|
end
|
253
250
|
latency = (Time.now - start) * 1000.0
|
254
251
|
# Measure
|
255
|
-
@
|
252
|
+
@metrics.time('sdk.' + calling_method, latency)
|
256
253
|
|
257
254
|
treatments_for_impressions = get_treatment_for_impressions(treatments_labels_change_numbers)
|
258
255
|
|
@@ -336,7 +333,7 @@ module SplitIoClient
|
|
336
333
|
store_impression(split_name, matching_key, bucketing_key, treatment_data, attributes) if store_impressions
|
337
334
|
|
338
335
|
# Measure
|
339
|
-
@
|
336
|
+
@metrics.time('sdk.' + calling_method, latency) unless multiple
|
340
337
|
rescue StandardError => error
|
341
338
|
@config.log_found_exception(__method__.to_s, error)
|
342
339
|
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jwt'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module SplitIoClient
|
7
|
+
module Engine
|
8
|
+
class AuthApiClient
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@api_client = SplitIoClient::Api::Client.new(@config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate(api_key)
|
15
|
+
response = @api_client.get_api(@config.auth_service_url, api_key)
|
16
|
+
|
17
|
+
return process_success(response) if response.success?
|
18
|
+
|
19
|
+
if response.status >= 400 && response.status < 500
|
20
|
+
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
|
21
|
+
|
22
|
+
return { push_enabled: false, retry: false }
|
23
|
+
end
|
24
|
+
|
25
|
+
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
|
26
|
+
{ push_enabled: false, retry: true }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def expiration(token_decoded)
|
32
|
+
exp = token_decoded[0]['exp']
|
33
|
+
|
34
|
+
(exp - SplitIoClient::Constants::EXPIRATION_RATE) - Time.now.getutc.to_i unless exp.nil?
|
35
|
+
rescue StandardError => e
|
36
|
+
@config.logger.error("Error getting token expiration: #{e.inspect}")
|
37
|
+
end
|
38
|
+
|
39
|
+
def channels(token_decoded)
|
40
|
+
capability = token_decoded[0]['x-ably-capability']
|
41
|
+
channels_hash = JSON.parse(capability)
|
42
|
+
channels_string = channels_hash.keys.join(',')
|
43
|
+
channels_string = control_channels(channels_string)
|
44
|
+
@config.logger.debug("Channels #{channels_string}") if @config.debug_enabled
|
45
|
+
CGI.escape(channels_string)
|
46
|
+
end
|
47
|
+
|
48
|
+
def decode_token(token)
|
49
|
+
JWT.decode token, nil, false
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_success(response)
|
53
|
+
@config.logger.debug("Success connection to: #{@config.auth_service_url}") if @config.debug_enabled
|
54
|
+
|
55
|
+
body_json = JSON.parse(response.body, symbolize_names: true)
|
56
|
+
push_enabled = body_json[:pushEnabled]
|
57
|
+
token = body_json[:token]
|
58
|
+
|
59
|
+
if push_enabled
|
60
|
+
decoded_token = decode_token(token)
|
61
|
+
channels = channels(decoded_token)
|
62
|
+
exp = expiration(decoded_token)
|
63
|
+
end
|
64
|
+
|
65
|
+
{ push_enabled: push_enabled, token: token, channels: channels, exp: exp, retry: false }
|
66
|
+
end
|
67
|
+
|
68
|
+
def control_channels(channels_string)
|
69
|
+
prefix = SplitIoClient::Constants::OCCUPANCY_CHANNEL_PREFIX
|
70
|
+
control_pri = SplitIoClient::Constants::CONTROL_PRI
|
71
|
+
control_sec = SplitIoClient::Constants::CONTROL_SEC
|
72
|
+
channels_string = channels_string.gsub(control_pri, "#{prefix}#{control_pri}")
|
73
|
+
channels_string = channels_string.gsub(control_sec, "#{prefix}#{control_sec}")
|
74
|
+
channels_string
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Engine
|
5
|
+
class PushManager
|
6
|
+
def initialize(config, sse_handler, api_key)
|
7
|
+
@config = config
|
8
|
+
@sse_handler = sse_handler
|
9
|
+
@auth_api_client = AuthApiClient.new(@config)
|
10
|
+
@api_key = api_key
|
11
|
+
@back_off = SplitIoClient::SSE::EventSource::BackOff.new(@config.auth_retry_back_off_base, 1)
|
12
|
+
end
|
13
|
+
|
14
|
+
def start_sse
|
15
|
+
response = @auth_api_client.authenticate(@api_key)
|
16
|
+
|
17
|
+
@config.logger.debug("Auth service response push_enabled: #{response[:push_enabled]}") if @config.debug_enabled
|
18
|
+
if response[:push_enabled]
|
19
|
+
@sse_handler.start(response[:token], response[:channels])
|
20
|
+
schedule_next_token_refresh(response[:exp])
|
21
|
+
@back_off.reset
|
22
|
+
else
|
23
|
+
stop_sse
|
24
|
+
end
|
25
|
+
|
26
|
+
schedule_next_token_refresh(@back_off.interval) if response[:retry]
|
27
|
+
rescue StandardError => e
|
28
|
+
@config.logger.error("start_sse: #{e.inspect}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop_sse
|
32
|
+
@sse_handler.process_disconnect if @sse_handler.sse_client.nil?
|
33
|
+
@sse_handler.stop
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def schedule_next_token_refresh(time)
|
39
|
+
@config.threads[:schedule_next_token_refresh] = Thread.new do
|
40
|
+
begin
|
41
|
+
@config.logger.debug("schedule_next_token_refresh refresh in #{time} seconds.") if @config.debug_enabled
|
42
|
+
sleep(time)
|
43
|
+
@config.logger.debug('schedule_next_token_refresh starting ...') if @config.debug_enabled
|
44
|
+
stop_sse
|
45
|
+
start_sse
|
46
|
+
rescue StandardError => e
|
47
|
+
@config.logger.debug("schedule_next_token_refresh error: #{e.inspect}") if @config.debug_enabled
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Engine
|
5
|
+
class SyncManager
|
6
|
+
include SplitIoClient::Cache::Fetchers
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
repositories,
|
10
|
+
api_key,
|
11
|
+
config,
|
12
|
+
sdk_blocker,
|
13
|
+
metrics
|
14
|
+
)
|
15
|
+
split_fetcher = SplitFetcher.new(repositories[:splits], api_key, metrics, config, sdk_blocker)
|
16
|
+
segment_fetcher = SegmentFetcher.new(repositories[:segments], api_key, metrics, config, sdk_blocker)
|
17
|
+
sync_params = { split_fetcher: split_fetcher, segment_fetcher: segment_fetcher }
|
18
|
+
|
19
|
+
@synchronizer = Synchronizer.new(repositories, api_key, config, sdk_blocker, sync_params)
|
20
|
+
notification_manager_keeper = SplitIoClient::SSE::NotificationManagerKeeper.new(config) do |manager|
|
21
|
+
manager.on_occupancy { |publisher_available| process_occupancy(publisher_available) }
|
22
|
+
manager.on_push_shutdown { process_push_shutdown }
|
23
|
+
end
|
24
|
+
@sse_handler = SplitIoClient::SSE::SSEHandler.new(
|
25
|
+
config,
|
26
|
+
@synchronizer,
|
27
|
+
repositories[:splits],
|
28
|
+
repositories[:segments],
|
29
|
+
notification_manager_keeper
|
30
|
+
) do |handler|
|
31
|
+
handler.on_connected { process_connected }
|
32
|
+
handler.on_disconnect { process_disconnect }
|
33
|
+
end
|
34
|
+
|
35
|
+
@push_manager = PushManager.new(config, @sse_handler, api_key)
|
36
|
+
@config = config
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
if @config.streaming_enabled
|
41
|
+
start_stream
|
42
|
+
start_stream_forked if defined?(PhusionPassenger)
|
43
|
+
elsif @config.standalone?
|
44
|
+
start_poll
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Starts tasks if stream is enabled.
|
51
|
+
def start_stream
|
52
|
+
@config.logger.debug('Starting push mode ...')
|
53
|
+
stream_start_thread
|
54
|
+
@synchronizer.start_periodic_data_recording
|
55
|
+
|
56
|
+
stream_start_sse_thread
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_poll
|
60
|
+
@config.logger.debug('Starting polling mode ...')
|
61
|
+
@synchronizer.start_periodic_fetch
|
62
|
+
@synchronizer.start_periodic_data_recording
|
63
|
+
rescue StandardError => e
|
64
|
+
@config.logger.error("start_poll error : #{e.inspect}")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Starts thread which fetch splits and segments once and trigger task to periodic data recording.
|
68
|
+
def stream_start_thread
|
69
|
+
@config.threads[:sync_manager_start_stream] = Thread.new do
|
70
|
+
begin
|
71
|
+
@synchronizer.sync_all
|
72
|
+
rescue StandardError => e
|
73
|
+
@config.logger.error("stream_start_thread error : #{e.inspect}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Starts thread which connect to sse and after that fetch splits and segments once.
|
79
|
+
def stream_start_sse_thread
|
80
|
+
@config.threads[:sync_manager_start_sse] = Thread.new do
|
81
|
+
begin
|
82
|
+
@push_manager.start_sse
|
83
|
+
rescue StandardError => e
|
84
|
+
@config.logger.error("stream_start_sse_thread error : #{e.inspect}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def start_stream_forked
|
90
|
+
PhusionPassenger.on_event(:starting_worker_process) { |forked| start_stream if forked }
|
91
|
+
end
|
92
|
+
|
93
|
+
def process_connected
|
94
|
+
@synchronizer.stop_periodic_fetch
|
95
|
+
@synchronizer.sync_all
|
96
|
+
@sse_handler.start_workers
|
97
|
+
rescue StandardError => e
|
98
|
+
@config.logger.error("process_connected error: #{e.inspect}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_disconnect
|
102
|
+
@sse_handler.stop_workers
|
103
|
+
@synchronizer.start_periodic_fetch
|
104
|
+
rescue StandardError => e
|
105
|
+
@config.logger.error("process_disconnect error: #{e.inspect}")
|
106
|
+
end
|
107
|
+
|
108
|
+
def process_occupancy(push_enable)
|
109
|
+
process_disconnect unless push_enable
|
110
|
+
process_connected if push_enable
|
111
|
+
rescue StandardError => e
|
112
|
+
@config.logger.error("process_occupancy error: #{e.inspect}")
|
113
|
+
end
|
114
|
+
|
115
|
+
def process_push_shutdown
|
116
|
+
@push_manager.stop_sse
|
117
|
+
process_disconnect
|
118
|
+
rescue StandardError => e
|
119
|
+
@config.logger.error("process_push_shutdown error: #{e.inspect}")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Engine
|
5
|
+
class Synchronizer
|
6
|
+
include SplitIoClient::Cache::Fetchers
|
7
|
+
include SplitIoClient::Cache::Senders
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
repositories,
|
11
|
+
api_key,
|
12
|
+
config,
|
13
|
+
sdk_blocker,
|
14
|
+
params
|
15
|
+
)
|
16
|
+
@splits_repository = repositories[:splits]
|
17
|
+
@segments_repository = repositories[:segments]
|
18
|
+
@impressions_repository = repositories[:impressions]
|
19
|
+
@metrics_repository = repositories[:metrics]
|
20
|
+
@events_repository = repositories[:events]
|
21
|
+
@api_key = api_key
|
22
|
+
@config = config
|
23
|
+
@sdk_blocker = sdk_blocker
|
24
|
+
@split_fetcher = params[:split_fetcher]
|
25
|
+
@segment_fetcher = params[:segment_fetcher]
|
26
|
+
end
|
27
|
+
|
28
|
+
def sync_all
|
29
|
+
@config.logger.debug('Synchronizing Splits and Segments ...') if @config.debug_enabled
|
30
|
+
fetch_splits
|
31
|
+
fetch_segments
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_periodic_data_recording
|
35
|
+
impressions_sender
|
36
|
+
metrics_sender
|
37
|
+
events_sender
|
38
|
+
end
|
39
|
+
|
40
|
+
def start_periodic_fetch
|
41
|
+
@split_fetcher.call
|
42
|
+
@segment_fetcher.call
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_periodic_fetch
|
46
|
+
@split_fetcher.stop_splits_thread
|
47
|
+
@segment_fetcher.stop_segments_thread
|
48
|
+
end
|
49
|
+
|
50
|
+
def fetch_splits
|
51
|
+
back_off = SplitIoClient::SSE::EventSource::BackOff.new(SplitIoClient::Constants::FETCH_BACK_OFF_BASE_RETRIES, 1)
|
52
|
+
loop do
|
53
|
+
break if @split_fetcher.fetch_splits
|
54
|
+
|
55
|
+
sleep(back_off.interval)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fetch_segment(name)
|
60
|
+
back_off = SplitIoClient::SSE::EventSource::BackOff.new(SplitIoClient::Constants::FETCH_BACK_OFF_BASE_RETRIES, 1)
|
61
|
+
loop do
|
62
|
+
break if @segment_fetcher.fetch_segment(name)
|
63
|
+
|
64
|
+
sleep(back_off.interval)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def fetch_segments
|
71
|
+
@segment_fetcher.fetch_segments
|
72
|
+
end
|
73
|
+
|
74
|
+
# Starts thread which loops constantly and sends impressions to the Split API
|
75
|
+
def impressions_sender
|
76
|
+
ImpressionsSender.new(@impressions_repository, @api_key, @config).call
|
77
|
+
end
|
78
|
+
|
79
|
+
# Starts thread which loops constantly and sends metrics to the Split API
|
80
|
+
def metrics_sender
|
81
|
+
MetricsSender.new(@metrics_repository, @api_key, @config).call
|
82
|
+
end
|
83
|
+
|
84
|
+
# Starts thread which loops constantly and sends events to the Split API
|
85
|
+
def events_sender
|
86
|
+
EventsSender.new(@events_repository, @config).call
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -4,4 +4,12 @@ module SplitIoClient
|
|
4
4
|
class SDKShutdownException < SplitIoError; end
|
5
5
|
|
6
6
|
class SDKBlockerTimeoutExpiredException < SplitIoError; end
|
7
|
+
|
8
|
+
class SSEClientException < SplitIoError
|
9
|
+
attr_reader :event
|
10
|
+
|
11
|
+
def initialize(event)
|
12
|
+
@event = event
|
13
|
+
end
|
14
|
+
end
|
7
15
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Helpers
|
5
|
+
class ThreadHelper
|
6
|
+
def self.stop(thread_sym, config)
|
7
|
+
thread = config.threads[thread_sym]
|
8
|
+
|
9
|
+
unless thread.nil?
|
10
|
+
config.logger.debug("Stopping #{thread_sym} thread...") if config.debug_enabled
|
11
|
+
sleep(0.1) while thread.status == 'run'
|
12
|
+
Thread.kill(thread)
|
13
|
+
end
|
14
|
+
rescue StandardError => e
|
15
|
+
config.logger.error(e.inspect)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.alive?(thread_sym, config)
|
19
|
+
thread = config.threads[thread_sym]
|
20
|
+
|
21
|
+
thread.nil? ? false : thread.alive?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|