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
@@ -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
|