splitclient-rb 7.0.4.pre.rc3-java → 7.1.0-java
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/.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
@@ -102,7 +102,11 @@ module SplitIoClient
|
|
102
102
|
@split_validator = SplitIoClient::Validators.new(self)
|
103
103
|
@localhost_mode = opts[:localhost_mode]
|
104
104
|
|
105
|
-
@
|
105
|
+
@streaming_enabled = consumer? ? false : (opts[:streaming_enabled].nil? ? SplitConfig.default_streaming_enabled : opts[:streaming_enabled])
|
106
|
+
@streaming_service_url = opts[:streaming_service_url] || SplitConfig.default_streaming_service_url
|
107
|
+
@auth_service_url = opts[:auth_service_url] || SplitConfig.default_auth_service_url
|
108
|
+
@auth_retry_back_off_base = SplitConfig.init_auth_retry_back_off(opts[:auth_retry_back_off_base] || SplitConfig.default_auth_retry_back_off_base)
|
109
|
+
@streaming_reconnect_back_off_base = SplitConfig.init_streaming_reconnect_back_off(opts[:streaming_reconnect_back_off_base] || SplitConfig.default_streaming_reconnect_back_off_base)
|
106
110
|
|
107
111
|
startup_log
|
108
112
|
end
|
@@ -256,7 +260,43 @@ module SplitIoClient
|
|
256
260
|
|
257
261
|
attr_accessor :ip_addresses_enabled
|
258
262
|
|
259
|
-
attr_accessor :
|
263
|
+
attr_accessor :auth_service_url
|
264
|
+
|
265
|
+
attr_accessor :auth_retry_back_off_base
|
266
|
+
|
267
|
+
attr_accessor :streaming_service_url
|
268
|
+
|
269
|
+
attr_accessor :streaming_reconnect_back_off_base
|
270
|
+
|
271
|
+
attr_accessor :streaming_enabled
|
272
|
+
|
273
|
+
def self.default_streaming_enabled
|
274
|
+
false
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.default_streaming_service_url
|
278
|
+
'https://split-realtime.ably.io/event-stream'
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.default_auth_service_url
|
282
|
+
'https://auth.split.io/api/auth'
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.default_auth_retry_back_off_base
|
286
|
+
1
|
287
|
+
end
|
288
|
+
|
289
|
+
def self.default_streaming_reconnect_back_off_base
|
290
|
+
1
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.init_auth_retry_back_off(auth_retry_back_off)
|
294
|
+
auth_retry_back_off < 1 ? SplitConfig.default_auth_retry_back_off_base : auth_retry_back_off
|
295
|
+
end
|
296
|
+
|
297
|
+
def self.init_streaming_reconnect_back_off(streaming_reconnect_back_off)
|
298
|
+
streaming_reconnect_back_off < 1 ? SplitConfig.default_streaming_reconnect_back_off_base : streaming_reconnect_back_off
|
299
|
+
end
|
260
300
|
|
261
301
|
#
|
262
302
|
# The default split client configuration
|
@@ -385,8 +425,6 @@ module SplitIoClient
|
|
385
425
|
end
|
386
426
|
end
|
387
427
|
|
388
|
-
|
389
|
-
|
390
428
|
#
|
391
429
|
# The default debug value
|
392
430
|
#
|
@@ -6,6 +6,7 @@ module SplitIoClient
|
|
6
6
|
|
7
7
|
include SplitIoClient::Cache::Repositories
|
8
8
|
include SplitIoClient::Cache::Stores
|
9
|
+
include SplitIoClient::Cache::Senders
|
9
10
|
|
10
11
|
attr_reader :adapter, :client, :manager, :config
|
11
12
|
|
@@ -29,16 +30,14 @@ module SplitIoClient
|
|
29
30
|
@splits_repository = SplitsRepository.new(@config)
|
30
31
|
@segments_repository = SegmentsRepository.new(@config)
|
31
32
|
@impressions_repository = ImpressionsRepository.new(@config)
|
32
|
-
@metrics_repository = MetricsRepository.new(@config)
|
33
33
|
@events_repository = EventsRepository.new(@config, @api_key)
|
34
|
-
|
34
|
+
@metrics_repository = MetricsRepository.new(@config)
|
35
35
|
@sdk_blocker = SDKBlocker.new(@splits_repository, @segments_repository, @config)
|
36
|
+
@metrics = Metrics.new(100, @metrics_repository)
|
36
37
|
|
37
|
-
|
38
|
+
start!
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
@client = SplitClient.new(@api_key, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config, @sse_handler)
|
40
|
+
@client = SplitClient.new(@api_key, @metrics, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config)
|
42
41
|
@manager = SplitManager.new(@splits_repository, @sdk_blocker, @config)
|
43
42
|
|
44
43
|
validate_api_key
|
@@ -49,7 +48,11 @@ module SplitIoClient
|
|
49
48
|
end
|
50
49
|
|
51
50
|
def start!
|
52
|
-
|
51
|
+
if @config.localhost_mode
|
52
|
+
start_localhost_components
|
53
|
+
else
|
54
|
+
SplitIoClient::Engine::SyncManager.new(repositories, @api_key, @config, @sdk_blocker, @metrics).start
|
55
|
+
end
|
53
56
|
end
|
54
57
|
|
55
58
|
def stop!
|
@@ -118,15 +121,22 @@ module SplitIoClient
|
|
118
121
|
end
|
119
122
|
end
|
120
123
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
def repositories
|
125
|
+
repos = {}
|
126
|
+
repos[:splits] = @splits_repository
|
127
|
+
repos[:segments] = @segments_repository
|
128
|
+
repos[:impressions] = @impressions_repository
|
129
|
+
repos[:events] = @events_repository
|
130
|
+
repos[:metrics] = @metrics_repository
|
126
131
|
|
127
|
-
|
128
|
-
|
129
|
-
|
132
|
+
repos
|
133
|
+
end
|
134
|
+
|
135
|
+
def start_localhost_components
|
136
|
+
LocalhostSplitStore.new(@splits_repository, @config, @sdk_blocker).call
|
137
|
+
|
138
|
+
# Starts thread which loops constantly and cleans up repositories to avoid memory issues in localhost mode
|
139
|
+
LocalhostRepoCleaner.new(@impressions_repository, @metrics_repository, @events_repository, @config).call
|
130
140
|
end
|
131
141
|
end
|
132
142
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module SSE
|
5
|
+
module EventSource
|
6
|
+
class BackOff
|
7
|
+
def initialize(back_off_base, attempt = 0)
|
8
|
+
@attempt = attempt
|
9
|
+
@back_off_base = back_off_base
|
10
|
+
end
|
11
|
+
|
12
|
+
def interval
|
13
|
+
interval = (@back_off_base * (2**@attempt)) if @attempt.positive?
|
14
|
+
@attempt += 1
|
15
|
+
|
16
|
+
interval || 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@attempt = 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -8,107 +8,117 @@ module SplitIoClient
|
|
8
8
|
module SSE
|
9
9
|
module EventSource
|
10
10
|
class Client
|
11
|
-
DEFAULT_READ_TIMEOUT =
|
11
|
+
DEFAULT_READ_TIMEOUT = 70
|
12
12
|
KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
|
13
13
|
|
14
|
-
def initialize(
|
15
|
-
@uri = URI(url)
|
14
|
+
def initialize(config, read_timeout: DEFAULT_READ_TIMEOUT)
|
16
15
|
@config = config
|
17
16
|
@read_timeout = read_timeout
|
18
17
|
@connected = Concurrent::AtomicBoolean.new(false)
|
19
18
|
@socket = nil
|
19
|
+
@back_off = BackOff.new(@config.streaming_reconnect_back_off_base)
|
20
20
|
|
21
|
-
@on = { event: ->(_) {},
|
21
|
+
@on = { event: ->(_) {}, connected: ->(_) {}, disconnect: ->(_) {} }
|
22
22
|
|
23
23
|
yield self if block_given?
|
24
|
-
|
25
|
-
connect_thread
|
26
|
-
|
27
|
-
connect_passenger_forked if defined?(PhusionPassenger)
|
28
24
|
end
|
29
25
|
|
30
26
|
def on_event(&action)
|
31
27
|
@on[:event] = action
|
32
28
|
end
|
33
29
|
|
34
|
-
def
|
35
|
-
@on[:
|
30
|
+
def on_connected(&action)
|
31
|
+
@on[:connected] = action
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_disconnect(&action)
|
35
|
+
@on[:disconnect] = action
|
36
36
|
end
|
37
37
|
|
38
38
|
def close
|
39
|
+
dispatch_disconnect
|
39
40
|
@connected.make_false
|
41
|
+
SplitIoClient::Helpers::ThreadHelper.stop(:connect_stream, @config)
|
40
42
|
@socket&.close
|
41
|
-
|
43
|
+
rescue StandardError => e
|
44
|
+
@config.logger.error("SSEClient close Error: #{e.inspect}")
|
42
45
|
end
|
43
46
|
|
44
|
-
def
|
45
|
-
|
47
|
+
def start(url)
|
48
|
+
@uri = URI(url)
|
46
49
|
|
47
|
-
|
50
|
+
connect_thread
|
51
|
+
rescue StandardError => e
|
52
|
+
@config.logger.error("SSEClient start Error: #{e.inspect}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def connected?
|
56
|
+
@connected.value
|
48
57
|
end
|
49
58
|
|
50
59
|
private
|
51
60
|
|
52
61
|
def connect_thread
|
53
62
|
@config.threads[:connect_stream] = Thread.new do
|
63
|
+
@config.logger.info('Starting connect_stream thread ...') if @config.debug_enabled
|
54
64
|
connect_stream
|
55
65
|
end
|
56
66
|
end
|
57
67
|
|
58
|
-
def connect_passenger_forked
|
59
|
-
PhusionPassenger.on_event(:starting_worker_process) { |forked| connect_thread if forked }
|
60
|
-
end
|
61
|
-
|
62
68
|
def connect_stream
|
63
|
-
|
69
|
+
interval = @back_off.interval
|
70
|
+
sleep(interval) if interval.positive?
|
64
71
|
|
65
|
-
|
66
|
-
@socket = socket_connect
|
67
|
-
|
68
|
-
@socket.write(build_request(@uri))
|
69
|
-
@connected.make_true
|
70
|
-
rescue StandardError => e
|
71
|
-
dispatch_error(e.inspect)
|
72
|
-
end
|
72
|
+
socket_write
|
73
73
|
|
74
74
|
while @connected.value
|
75
75
|
begin
|
76
|
-
partial_data = @socket.readpartial(
|
77
|
-
|
78
|
-
|
76
|
+
partial_data = @socket.readpartial(10_000, timeout: @read_timeout)
|
77
|
+
|
78
|
+
raise 'eof exception' if partial_data == :eof
|
79
|
+
rescue StandardError => e
|
80
|
+
@config.logger.error(e.inspect) if @config.debug_enabled
|
79
81
|
@connected.make_false
|
82
|
+
@socket&.close
|
83
|
+
@socket = nil
|
80
84
|
connect_stream
|
81
85
|
end
|
82
86
|
|
83
|
-
|
87
|
+
process_data(partial_data)
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
91
|
+
def socket_write
|
92
|
+
@socket = socket_connect
|
93
|
+
@socket.write(build_request(@uri))
|
94
|
+
dispatch_connected
|
95
|
+
rescue StandardError => e
|
96
|
+
@config.logger.error("Error during connecting to #{@uri.host}. Error: #{e.inspect}")
|
97
|
+
close
|
98
|
+
end
|
99
|
+
|
87
100
|
def socket_connect
|
88
101
|
return Socketry::SSL::Socket.connect(@uri.host, @uri.port) if @uri.scheme.casecmp('https').zero?
|
89
102
|
|
90
103
|
Socketry::TCP::Socket.connect(@uri.host, @uri.port)
|
91
104
|
end
|
92
105
|
|
93
|
-
def
|
94
|
-
unless partial_data.nil?
|
95
|
-
@config.logger.debug("Event partial data: #{partial_data}")
|
106
|
+
def process_data(partial_data)
|
107
|
+
unless partial_data.nil? || partial_data == KEEP_ALIVE_RESPONSE
|
108
|
+
@config.logger.debug("Event partial data: #{partial_data}") if @config.debug_enabled
|
96
109
|
buffer = read_partial_data(partial_data)
|
97
|
-
|
98
|
-
|
99
|
-
dispatch_event(event)
|
110
|
+
parse_event(buffer)
|
100
111
|
end
|
101
112
|
rescue StandardError => e
|
102
|
-
|
113
|
+
@config.logger.error("process_data error: #{e.inspect}")
|
103
114
|
end
|
104
115
|
|
105
116
|
def build_request(uri)
|
106
117
|
req = "GET #{uri.request_uri} HTTP/1.1\r\n"
|
107
118
|
req << "Host: #{uri.host}\r\n"
|
108
119
|
req << "Accept: text/event-stream\r\n"
|
109
|
-
req << "Cache-Control: no-cache\r\n"
|
110
|
-
req
|
111
|
-
@config.logger.debug("Request info: #{req}")
|
120
|
+
req << "Cache-Control: no-cache\r\n\r\n"
|
121
|
+
@config.logger.debug("Request info: #{req}") if @config.debug_enabled
|
112
122
|
req
|
113
123
|
end
|
114
124
|
|
@@ -120,43 +130,58 @@ module SplitIoClient
|
|
120
130
|
end
|
121
131
|
|
122
132
|
def parse_event(buffer)
|
123
|
-
|
124
|
-
parsed_data = nil
|
125
|
-
client_id = nil
|
133
|
+
type = nil
|
126
134
|
|
127
135
|
buffer.each do |d|
|
128
136
|
splited_data = d.split(':')
|
129
137
|
|
130
138
|
case splited_data[0]
|
131
139
|
when 'event'
|
132
|
-
|
140
|
+
type = splited_data[1].strip
|
133
141
|
when 'data'
|
134
|
-
|
135
|
-
|
136
|
-
|
142
|
+
data = parse_event_data(d, type)
|
143
|
+
unless type.nil? || data[:data].nil?
|
144
|
+
event = StreamData.new(type, data[:client_id], data[:data], data[:channel])
|
145
|
+
dispatch_event(event)
|
146
|
+
end
|
137
147
|
end
|
138
148
|
end
|
149
|
+
rescue StandardError => e
|
150
|
+
@config.logger.error("Error during parsing a event: #{e.inspect}")
|
151
|
+
end
|
139
152
|
|
140
|
-
|
153
|
+
def parse_event_data(data, type)
|
154
|
+
event_data = JSON.parse(data.sub('data: ', ''))
|
155
|
+
client_id = event_data['clientId']&.strip
|
156
|
+
channel = event_data['channel']&.strip
|
157
|
+
parsed_data = JSON.parse(event_data['data']) unless type == 'error'
|
158
|
+
parsed_data = event_data if type == 'error'
|
141
159
|
|
142
|
-
|
143
|
-
rescue StandardError => e
|
144
|
-
dispatch_error(e.inspect)
|
145
|
-
nil
|
160
|
+
{ client_id: client_id, channel: channel, data: parsed_data }
|
146
161
|
end
|
147
162
|
|
148
163
|
def dispatch_event(event)
|
149
|
-
|
150
|
-
|
164
|
+
raise SSEClientException.new(event), 'Error event' if event.event_type == 'error'
|
165
|
+
|
166
|
+
@config.logger.debug("Dispatching event: #{event.event_type}, #{event.channel}") if @config.debug_enabled
|
167
|
+
@on[:event].call(event)
|
168
|
+
rescue SSEClientException => e
|
169
|
+
@config.logger.error("Event error: #{e.event.event_type}, #{e.event.data}")
|
170
|
+
close
|
151
171
|
end
|
152
172
|
|
153
|
-
def
|
154
|
-
@
|
155
|
-
@
|
173
|
+
def dispatch_connected
|
174
|
+
@connected.make_true
|
175
|
+
@back_off.reset
|
176
|
+
@config.logger.debug('Dispatching connected') if @config.debug_enabled
|
177
|
+
@on[:connected].call
|
156
178
|
end
|
157
|
-
end
|
158
179
|
|
159
|
-
|
180
|
+
def dispatch_disconnect
|
181
|
+
@config.logger.debug('Dispatching disconnect') if @config.debug_enabled
|
182
|
+
@on[:disconnect].call
|
183
|
+
end
|
184
|
+
end
|
160
185
|
end
|
161
186
|
end
|
162
187
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module SSE
|
5
|
+
module EventSource
|
6
|
+
class StreamData
|
7
|
+
attr_reader :event_type, :channel, :data, :client_id
|
8
|
+
|
9
|
+
def initialize(event_type, client_id, data, channel)
|
10
|
+
@event_type = event_type
|
11
|
+
@client_id = client_id
|
12
|
+
@data = data
|
13
|
+
@channel = channel&.gsub(SplitIoClient::Constants::OCCUPANCY_CHANNEL_PREFIX, '')
|
14
|
+
end
|
15
|
+
|
16
|
+
def occupancy?
|
17
|
+
@channel.include? 'control'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent/atomics'
|
4
|
+
|
5
|
+
module SplitIoClient
|
6
|
+
module SSE
|
7
|
+
class NotificationManagerKeeper
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@publisher_available = Concurrent::AtomicBoolean.new(true)
|
11
|
+
@on = { occupancy: ->(_) {}, push_shutdown: ->(_) {} }
|
12
|
+
|
13
|
+
yield self if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def handle_incoming_occupancy_event(event)
|
17
|
+
if event.data['type'] == 'CONTROL'
|
18
|
+
process_event_control(event.data['controlType'])
|
19
|
+
elsif event.channel == SplitIoClient::Constants::CONTROL_PRI
|
20
|
+
process_event_occupancy(event.data['metrics']['publishers'])
|
21
|
+
end
|
22
|
+
rescue StandardError => e
|
23
|
+
@config.logger.error(e)
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_occupancy(&action)
|
27
|
+
@on[:occupancy] = action
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_push_shutdown(&action)
|
31
|
+
@on[:push_shutdown] = action
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def process_event_control(type)
|
37
|
+
case type
|
38
|
+
when 'STREAMING_PAUSED'
|
39
|
+
dispatch_occupancy_event(false)
|
40
|
+
when 'STREAMING_RESUMED'
|
41
|
+
dispatch_occupancy_event(true) if @publisher_available.value
|
42
|
+
when 'STREAMING_DISABLED'
|
43
|
+
dispatch_push_shutdown
|
44
|
+
else
|
45
|
+
@config.logger.error("Incorrect event type: #{incoming_notification}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_event_occupancy(publishers)
|
50
|
+
@config.logger.debug("Occupancy process event with #{publishers} publishers") if @config.debug_enabled
|
51
|
+
if publishers <= 0 && @publisher_available.value
|
52
|
+
@publisher_available.make_false
|
53
|
+
dispatch_occupancy_event(false)
|
54
|
+
elsif publishers >= 1 && !@publisher_available.value
|
55
|
+
@publisher_available.make_true
|
56
|
+
dispatch_occupancy_event(true)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def dispatch_occupancy_event(push_enable)
|
61
|
+
@config.logger.debug("Dispatching occupancy event with publisher avaliable: #{push_enable}")
|
62
|
+
@on[:occupancy].call(push_enable)
|
63
|
+
end
|
64
|
+
|
65
|
+
def dispatch_push_shutdown
|
66
|
+
@config.logger.debug('Dispatching push shutdown')
|
67
|
+
@on[:push_shutdown].call
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module SSE
|
5
|
+
class NotificationProcessor
|
6
|
+
def initialize(config, splits_worker, segments_worker)
|
7
|
+
@config = config
|
8
|
+
@splits_worker = splits_worker
|
9
|
+
@segments_worker = segments_worker
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(incoming_notification)
|
13
|
+
case incoming_notification.data['type']
|
14
|
+
when SSE::EventSource::EventTypes::SPLIT_UPDATE
|
15
|
+
process_split_update(incoming_notification)
|
16
|
+
when SSE::EventSource::EventTypes::SPLIT_KILL
|
17
|
+
process_split_kill(incoming_notification)
|
18
|
+
when SSE::EventSource::EventTypes::SEGMENT_UPDATE
|
19
|
+
process_segment_update(incoming_notification)
|
20
|
+
else
|
21
|
+
@config.logger.error("Incorrect event type: #{incoming_notification}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def process_split_update(notification)
|
28
|
+
@config.logger.debug("SPLIT UPDATE notification received: #{notification}") if @config.debug_enabled
|
29
|
+
@splits_worker.add_to_queue(notification.data['changeNumber'])
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_split_kill(notification)
|
33
|
+
@config.logger.debug("SPLIT KILL notification received: #{notification}") if @config.debug_enabled
|
34
|
+
change_number = notification.data['changeNumber']
|
35
|
+
default_treatment = notification.data['defaultTreatment']
|
36
|
+
split_name = notification.data['splitName']
|
37
|
+
|
38
|
+
@splits_worker.kill_split(change_number, split_name, default_treatment)
|
39
|
+
end
|
40
|
+
|
41
|
+
def process_segment_update(notification)
|
42
|
+
@config.logger.debug("SEGMENT UPDATE notification received: #{notification}") if @config.debug_enabled
|
43
|
+
change_number = notification.data['changeNumber']
|
44
|
+
segment_name = notification.data['segmentName']
|
45
|
+
|
46
|
+
@segments_worker.add_to_queue(change_number, segment_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -5,78 +5,73 @@ module SplitIoClient
|
|
5
5
|
class SSEHandler
|
6
6
|
attr_reader :sse_client
|
7
7
|
|
8
|
-
def initialize(config,
|
8
|
+
def initialize(config, synchronizer, splits_repository, segments_repository, notification_manager_keeper)
|
9
9
|
@config = config
|
10
|
-
@
|
11
|
-
@splits_worker =
|
12
|
-
@segments_worker =
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
@notification_manager_keeper = notification_manager_keeper
|
11
|
+
@splits_worker = SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository)
|
12
|
+
@segments_worker = SplitIoClient::SSE::Workers::SegmentsWorker.new(synchronizer, config, segments_repository)
|
13
|
+
@notification_processor = SplitIoClient::SSE::NotificationProcessor.new(config, @splits_worker, @segments_worker)
|
14
|
+
@sse_client = SSE::EventSource::Client.new(@config) do |client|
|
15
|
+
client.on_event { |event| handle_incoming_message(event) }
|
16
|
+
client.on_connected { process_connected }
|
17
|
+
client.on_disconnect { process_disconnect }
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
url = "#{@options[:url_host]}/event-stream?channels=#{@options[:channels]}&v=1.1&key=#{@options[:key]}"
|
20
|
+
@on = { connected: ->(_) {}, disconnect: ->(_) {} }
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
process_event(event)
|
26
|
-
end
|
22
|
+
yield self if block_given?
|
23
|
+
end
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
def start(token_jwt, channels)
|
26
|
+
url = "#{@config.streaming_service_url}?channels=#{channels}&v=1.1&accessToken=#{token_jwt}"
|
27
|
+
@sse_client.start(url)
|
28
|
+
end
|
32
29
|
|
33
|
-
|
30
|
+
def stop
|
31
|
+
@sse_client.close
|
32
|
+
stop_workers
|
33
|
+
rescue StandardError => e
|
34
|
+
@config.logger.debug("SSEHandler stop error: #{e.inspect}") if @config.debug_enabled
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
|
38
|
-
when SSE::EventSource::EventTypes::SPLIT_UPDATE
|
39
|
-
split_update_notification(event)
|
40
|
-
when SSE::EventSource::EventTypes::SPLIT_KILL
|
41
|
-
split_kill_notification(event)
|
42
|
-
when SSE::EventSource::EventTypes::SEGMENT_UPDATE
|
43
|
-
segment_update_notification(event)
|
44
|
-
when SSE::EventSource::EventTypes::CONTROL
|
45
|
-
control_notification(event)
|
46
|
-
else
|
47
|
-
@config.logger.error("Incorrect event type: #{event}")
|
48
|
-
end
|
37
|
+
def connected?
|
38
|
+
@sse_client&.connected? || false
|
49
39
|
end
|
50
40
|
|
51
|
-
def
|
52
|
-
@
|
41
|
+
def start_workers
|
42
|
+
@splits_worker.start
|
43
|
+
@segments_worker.start
|
53
44
|
end
|
54
45
|
|
55
|
-
def
|
56
|
-
@
|
57
|
-
@
|
46
|
+
def stop_workers
|
47
|
+
@splits_worker.stop
|
48
|
+
@segments_worker.stop
|
58
49
|
end
|
59
50
|
|
60
|
-
def
|
61
|
-
@
|
51
|
+
def on_connected(&action)
|
52
|
+
@on[:connected] = action
|
53
|
+
end
|
62
54
|
|
63
|
-
|
64
|
-
|
65
|
-
|
55
|
+
def on_disconnect(&action)
|
56
|
+
@on[:disconnect] = action
|
57
|
+
end
|
66
58
|
|
67
|
-
|
59
|
+
def process_disconnect
|
60
|
+
@on[:disconnect].call
|
68
61
|
end
|
69
62
|
|
70
|
-
|
71
|
-
@config.logger.debug("SEGMENT UPDATE notification received: #{event}")
|
72
|
-
change_number = event.data['changeNumber']
|
73
|
-
segment_name = event.data['segmentName']
|
63
|
+
private
|
74
64
|
|
75
|
-
|
65
|
+
def process_connected
|
66
|
+
@on[:connected].call
|
76
67
|
end
|
77
68
|
|
78
|
-
def
|
79
|
-
|
69
|
+
def handle_incoming_message(notification)
|
70
|
+
if notification.occupancy?
|
71
|
+
@notification_manager_keeper.handle_incoming_occupancy_event(notification)
|
72
|
+
else
|
73
|
+
@notification_processor.process(notification)
|
74
|
+
end
|
80
75
|
end
|
81
76
|
end
|
82
77
|
end
|