splitclient-rb 7.2.2 → 7.2.3.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a040c4445c4df1853bab3fa0c2a4bd70c3d41ae0172ef085faf933a6498b59ab
4
- data.tar.gz: 820a55037233d6f0f187cc303c86dd0169809433683f72ce8787439a6d502426
3
+ metadata.gz: f35241d91acb957b2e666ca2598021b66f8214d4d12980484b063723576dc13c
4
+ data.tar.gz: 1a833d613162de2aa18a3c925de083ddc2013bfe85630d57b2e9483a8364c357
5
5
  SHA512:
6
- metadata.gz: 7781b6e7695804d77eacd265312bb02a16411d88807e09079de3f34d7a4bb644de2f001522ca15769bbd3309f6acd58f0d95c40618ea0599e9138edfda1ba613
7
- data.tar.gz: 37add439d441be62cd7c6bda54cfd9de3521cac7a23996977df9867261d01487e9fec466b9767ab7a50a128830700c2efa755d9b1829298a63dc0c8b047e0765
6
+ metadata.gz: 231f86643247585e51cbb136790d71011764645edb87383c5641730b4e36952c1013dde5c06fbb46002c05b1fa07579a0b8579a3a4e859cc42862d0d2849316c
7
+ data.tar.gz: faff2962445b587f133d703ce19fec646a8495fbef7f34d8dd27d38614643bd517acf99d859b613220a4144c4f56be61c1ae048b526ef8e63a92134dfdcbda79
@@ -10,6 +10,9 @@ Metrics/MethodLength:
10
10
  Metrics/ClassLength:
11
11
  Max: 150
12
12
 
13
+ Metrics/CyclomaticComplexity:
14
+ Max: 8
15
+
13
16
  Metrics/LineLength:
14
17
  Max: 130
15
18
  Exclude:
@@ -6,5 +6,11 @@ class SplitIoClient::Constants
6
6
  CONTROL_SEC = 'control_sec'
7
7
  OCCUPANCY_CHANNEL_PREFIX = '[?occupancy=metrics.publishers]'
8
8
  FETCH_BACK_OFF_BASE_RETRIES = 1
9
+ PUSH_CONNECTED = 'PUSH_CONNECTED'
10
+ PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'
11
+ PUSH_NONRETRYABLE_ERROR = 'PUSH_NONRETRYABLE_ERROR'
12
+ PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN'
13
+ PUSH_SUBSYSTEM_READY = 'PUSH_SUBSYSTEM_READY'
14
+ PUSH_SUBSYSTEM_OFF = 'PUSH_SUBSYSTEM_OFF'
9
15
  end
10
16
 
@@ -19,12 +19,13 @@ module SplitIoClient
19
19
  if response[:push_enabled] && @sse_handler.start(response[:token], response[:channels])
20
20
  schedule_next_token_refresh(response[:exp])
21
21
  @back_off.reset
22
- return
22
+ return true
23
23
  end
24
24
 
25
25
  stop_sse
26
26
 
27
27
  schedule_next_token_refresh(@back_off.interval) if response[:retry]
28
+ false
28
29
  rescue StandardError => e
29
30
  @config.logger.error("start_sse: #{e.inspect}")
30
31
  end
@@ -11,8 +11,7 @@ module SplitIoClient
11
11
  )
12
12
  @synchronizer = synchronizer
13
13
  notification_manager_keeper = SplitIoClient::SSE::NotificationManagerKeeper.new(config) do |manager|
14
- manager.on_occupancy { |publisher_available| process_occupancy(publisher_available) }
15
- manager.on_push_shutdown { process_push_shutdown }
14
+ manager.on_action { |action| process_action(action) }
16
15
  end
17
16
  @sse_handler = SplitIoClient::SSE::SSEHandler.new(
18
17
  config,
@@ -21,8 +20,7 @@ module SplitIoClient
21
20
  repositories[:segments],
22
21
  notification_manager_keeper
23
22
  ) do |handler|
24
- handler.on_connected { process_connected }
25
- handler.on_disconnect { |reconnect| process_disconnect(reconnect) }
23
+ handler.on_action { |action| process_action(action) }
26
24
  end
27
25
 
28
26
  @push_manager = PushManager.new(config, @sse_handler, api_key)
@@ -44,10 +42,10 @@ module SplitIoClient
44
42
  # Starts tasks if stream is enabled.
45
43
  def start_stream
46
44
  @config.logger.debug('Starting push mode ...')
47
- stream_start_thread
45
+ sync_all_thread
48
46
  @synchronizer.start_periodic_data_recording
49
47
 
50
- stream_start_sse_thread
48
+ start_sse_connection_thread
51
49
  end
52
50
 
53
51
  def start_poll
@@ -59,23 +57,24 @@ module SplitIoClient
59
57
  end
60
58
 
61
59
  # Starts thread which fetch splits and segments once and trigger task to periodic data recording.
62
- def stream_start_thread
60
+ def sync_all_thread
63
61
  @config.threads[:sync_manager_start_stream] = Thread.new do
64
62
  begin
65
63
  @synchronizer.sync_all
66
64
  rescue StandardError => e
67
- @config.logger.error("stream_start_thread error : #{e.inspect}")
65
+ @config.logger.error("sync_all_thread error : #{e.inspect}")
68
66
  end
69
67
  end
70
68
  end
71
69
 
72
70
  # Starts thread which connect to sse and after that fetch splits and segments once.
73
- def stream_start_sse_thread
71
+ def start_sse_connection_thread
74
72
  @config.threads[:sync_manager_start_sse] = Thread.new do
75
73
  begin
76
- @push_manager.start_sse
74
+ connected = @push_manager.start_sse
75
+ @synchronizer.start_periodic_fetch unless connected
77
76
  rescue StandardError => e
78
- @config.logger.error("stream_start_sse_thread error : #{e.inspect}")
77
+ @config.logger.error("start_sse_connection_thread error : #{e.inspect}")
79
78
  end
80
79
  end
81
80
  end
@@ -84,6 +83,46 @@ module SplitIoClient
84
83
  PhusionPassenger.on_event(:starting_worker_process) { |forked| start_stream if forked }
85
84
  end
86
85
 
86
+ def process_action(action)
87
+ case action
88
+ when Constants::PUSH_CONNECTED
89
+ process_connected
90
+ when Constants::PUSH_RETRYABLE_ERROR
91
+ process_disconnect(true)
92
+ when Constants::PUSH_NONRETRYABLE_ERROR
93
+ process_disconnect(false)
94
+ when Constants::PUSH_SUBSYSTEM_DOWN
95
+ process_subsystem_down
96
+ when Constants::PUSH_SUBSYSTEM_READY
97
+ process_subsystem_ready
98
+ when Constants::PUSH_SUBSYSTEM_OFF
99
+ process_push_shutdown
100
+ else
101
+ @config.logger.debug('Incorrect action type.')
102
+ end
103
+ rescue StandardError => e
104
+ @config.logger.error("process_action error: #{e.inspect}")
105
+ end
106
+
107
+ def process_subsystem_ready
108
+ @synchronizer.stop_periodic_fetch
109
+ @synchronizer.sync_all
110
+ @sse_handler.start_workers
111
+ end
112
+
113
+ def process_subsystem_down
114
+ @sse_handler.stop_workers
115
+ @synchronizer.start_periodic_fetch
116
+ end
117
+
118
+ def process_push_shutdown
119
+ @push_manager.stop_sse
120
+ @sse_handler.stop_workers
121
+ @synchronizer.start_periodic_fetch
122
+ rescue StandardError => e
123
+ @config.logger.error("process_push_shutdown error: #{e.inspect}")
124
+ end
125
+
87
126
  def process_connected
88
127
  if @sse_connected.value
89
128
  @config.logger.debug('Streaming already connected.')
@@ -115,28 +154,6 @@ module SplitIoClient
115
154
  rescue StandardError => e
116
155
  @config.logger.error("process_disconnect error: #{e.inspect}")
117
156
  end
118
-
119
- def process_occupancy(push_enable)
120
- if push_enable
121
- @synchronizer.stop_periodic_fetch
122
- @synchronizer.sync_all
123
- @sse_handler.start_workers
124
- return
125
- end
126
-
127
- @sse_handler.stop_workers
128
- @synchronizer.start_periodic_fetch
129
- rescue StandardError => e
130
- @config.logger.error("process_occupancy error: #{e.inspect}")
131
- end
132
-
133
- def process_push_shutdown
134
- @push_manager.stop_sse
135
- @sse_handler.stop_workers
136
- @synchronizer.start_periodic_fetch
137
- rescue StandardError => e
138
- @config.logger.error("process_push_shutdown error: #{e.inspect}")
139
- end
140
157
  end
141
158
  end
142
159
  end
@@ -9,6 +9,7 @@ module SplitIoClient
9
9
  class Client
10
10
  DEFAULT_READ_TIMEOUT = 70
11
11
  CONNECT_TIMEOUT = 30_000
12
+ OK_CODE = 200
12
13
  KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
13
14
  ERROR_EVENT_TYPE = 'error'.freeze
14
15
 
@@ -16,9 +17,10 @@ module SplitIoClient
16
17
  @config = config
17
18
  @read_timeout = read_timeout
18
19
  @connected = Concurrent::AtomicBoolean.new(false)
20
+ @first_event = Concurrent::AtomicBoolean.new(true)
19
21
  @socket = nil
20
22
  @event_parser = SSE::EventSource::EventParser.new(config)
21
- @on = { event: ->(_) {}, connected: ->(_) {}, disconnect: ->(_) {} }
23
+ @on = { event: ->(_) {}, action: ->(_) {} }
22
24
 
23
25
  yield self if block_given?
24
26
  end
@@ -27,16 +29,12 @@ module SplitIoClient
27
29
  @on[:event] = action
28
30
  end
29
31
 
30
- def on_connected(&action)
31
- @on[:connected] = action
32
+ def on_action(&action)
33
+ @on[:action] = action
32
34
  end
33
35
 
34
- def on_disconnect(&action)
35
- @on[:disconnect] = action
36
- end
37
-
38
- def close(reconnect = false)
39
- dispatch_disconnect(reconnect)
36
+ def close(action = Constants::PUSH_NONRETRYABLE_ERROR)
37
+ dispatch_action(action)
40
38
  @connected.make_false
41
39
  SplitIoClient::Helpers::ThreadHelper.stop(:connect_stream, @config)
42
40
  @socket&.close
@@ -72,16 +70,18 @@ module SplitIoClient
72
70
  end
73
71
 
74
72
  def connect_stream(latch)
75
- socket_write(latch)
73
+ socket_write
76
74
 
77
- while @connected.value
75
+ while connected? || @first_event.value
78
76
  begin
79
77
  partial_data = @socket.readpartial(10_000, timeout: @read_timeout)
80
78
 
79
+ read_first_event(partial_data, latch)
80
+
81
81
  raise 'eof exception' if partial_data == :eof
82
82
  rescue StandardError => e
83
83
  @config.logger.error('Error reading partial data: ' + e.inspect) if @config.debug_enabled
84
- close(true) # close conexion & reconnect
84
+ close(Constants::PUSH_RETRYABLE_ERROR)
85
85
  return
86
86
  end
87
87
 
@@ -89,14 +89,31 @@ module SplitIoClient
89
89
  end
90
90
  end
91
91
 
92
- def socket_write(latch)
92
+ def socket_write
93
+ @first_event.make_true
93
94
  @socket = socket_connect
94
95
  @socket.write(build_request(@uri))
95
- dispatch_connected
96
96
  rescue StandardError => e
97
97
  @config.logger.error("Error during connecting to #{@uri.host}. Error: #{e.inspect}")
98
- close
99
- ensure
98
+ close(Constants::PUSH_NONRETRYABLE_ERROR)
99
+ end
100
+
101
+ def read_first_event(data, latch)
102
+ return unless @first_event.value
103
+
104
+ response_code = @event_parser.first_event(data)
105
+ @config.logger.debug("SSE client first event code: #{response_code}")
106
+
107
+ error_event = false
108
+ events = @event_parser.parse(data)
109
+ events.each { |e| error_event = true if e.event_type == ERROR_EVENT_TYPE }
110
+ @first_event.make_false
111
+
112
+ if response_code == OK_CODE && !error_event
113
+ @connected.make_true
114
+ dispatch_action(Constants::PUSH_CONNECTED)
115
+ end
116
+
100
117
  latch.count_down
101
118
  end
102
119
 
@@ -137,9 +154,9 @@ module SplitIoClient
137
154
  def dispatch_error(event)
138
155
  @config.logger.error("Event error: #{event.event_type}, #{event.data}")
139
156
  if event.data['code'] >= 40_140 && event.data['code'] <= 40_149
140
- close(true) # close conexion & reconnect
157
+ close(Constants::PUSH_RETRYABLE_ERROR)
141
158
  elsif event.data['code'] >= 40_000 && event.data['code'] <= 49_999
142
- close # close conexion
159
+ close(Constants::PUSH_NONRETRYABLE_ERROR)
143
160
  end
144
161
  end
145
162
 
@@ -148,15 +165,9 @@ module SplitIoClient
148
165
  @on[:event].call(event)
149
166
  end
150
167
 
151
- def dispatch_connected
152
- @connected.make_true
153
- @config.logger.debug('Dispatching connected') if @config.debug_enabled
154
- @on[:connected].call
155
- end
156
-
157
- def dispatch_disconnect(reconnect)
158
- @config.logger.debug('Dispatching disconnect') if @config.debug_enabled
159
- @on[:disconnect].call(reconnect)
168
+ def dispatch_action(action)
169
+ @config.logger.debug("Dispatching action: #{action}") if @config.debug_enabled
170
+ @on[:action].call(action)
160
171
  end
161
172
  end
162
173
  end
@@ -4,6 +4,8 @@ module SplitIoClient
4
4
  module SSE
5
5
  module EventSource
6
6
  class EventParser
7
+ BAD_REQUEST_CODE = 400
8
+
7
9
  def initialize(config)
8
10
  @config = config
9
11
  end
@@ -27,10 +29,17 @@ module SplitIoClient
27
29
 
28
30
  events
29
31
  rescue StandardError => e
30
- @config.logger.error("Error during parsing a event: #{e.inspect}")
32
+ @config.logger.debug("Error during parsing a event: #{e.inspect}")
31
33
  []
32
34
  end
33
35
 
36
+ def first_event(raw_data)
37
+ raw_data.split("\n")[0].split(' ')[1].to_i
38
+ rescue StandardError => e
39
+ @config.logger.debug("Error parsing first event: #{e.inspect}")
40
+ BAD_REQUEST_CODE
41
+ end
42
+
34
43
  private
35
44
 
36
45
  def parse_event_data(data, type)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/atomics'
3
+ require 'concurrent'
4
4
 
5
5
  module SplitIoClient
6
6
  module SSE
@@ -8,7 +8,9 @@ module SplitIoClient
8
8
  def initialize(config)
9
9
  @config = config
10
10
  @publisher_available = Concurrent::AtomicBoolean.new(true)
11
- @on = { occupancy: ->(_) {}, push_shutdown: ->(_) {} }
11
+ @publishers_pri = Concurrent::AtomicFixnum.new
12
+ @publishers_sec = Concurrent::AtomicFixnum.new
13
+ @on = { action: ->(_) {} }
12
14
 
13
15
  yield self if block_given?
14
16
  end
@@ -16,19 +18,15 @@ module SplitIoClient
16
18
  def handle_incoming_occupancy_event(event)
17
19
  if event.data['type'] == 'CONTROL'
18
20
  process_event_control(event.data['controlType'])
19
- elsif event.channel == SplitIoClient::Constants::CONTROL_PRI
20
- process_event_occupancy(event.data['metrics']['publishers'])
21
+ else
22
+ process_event_occupancy(event.channel, event.data['metrics']['publishers'])
21
23
  end
22
24
  rescue StandardError => e
23
25
  @config.logger.error(e)
24
26
  end
25
27
 
26
- def on_occupancy(&action)
27
- @on[:occupancy] = action
28
- end
29
-
30
- def on_push_shutdown(&action)
31
- @on[:push_shutdown] = action
28
+ def on_action(&action)
29
+ @on[:action] = action
32
30
  end
33
31
 
34
32
  private
@@ -36,35 +34,42 @@ module SplitIoClient
36
34
  def process_event_control(type)
37
35
  case type
38
36
  when 'STREAMING_PAUSED'
39
- dispatch_occupancy_event(false)
37
+ dispatch_action(Constants::PUSH_SUBSYSTEM_DOWN)
40
38
  when 'STREAMING_RESUMED'
41
- dispatch_occupancy_event(true) if @publisher_available.value
39
+ dispatch_action(Constants::PUSH_SUBSYSTEM_READY) if @publisher_available.value
42
40
  when 'STREAMING_DISABLED'
43
- dispatch_push_shutdown
41
+ dispatch_action(Constants::PUSH_SUBSYSTEM_OFF)
44
42
  else
45
43
  @config.logger.error("Incorrect event type: #{incoming_notification}")
46
44
  end
47
45
  end
48
46
 
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
47
+ def process_event_occupancy(channel, publishers)
48
+ @config.logger.debug("Processed occupancy event with #{publishers} publishers. Channel: #{channel}")
49
+
50
+ update_publishers(channel, publishers)
51
+
52
+ if !are_publishers_available? && @publisher_available.value
52
53
  @publisher_available.make_false
53
- dispatch_occupancy_event(false)
54
- elsif publishers >= 1 && !@publisher_available.value
54
+ dispatch_action(Constants::PUSH_SUBSYSTEM_DOWN)
55
+ elsif are_publishers_available? && !@publisher_available.value
55
56
  @publisher_available.make_true
56
- dispatch_occupancy_event(true)
57
+ dispatch_action(Constants::PUSH_SUBSYSTEM_READY)
57
58
  end
58
59
  end
59
60
 
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)
61
+ def update_publishers(channel, publishers)
62
+ @publishers_pri.value = publishers if channel == Constants::CONTROL_PRI
63
+ @publishers_sec.value = publishers if channel == Constants::CONTROL_SEC
64
+ end
65
+
66
+ def are_publishers_available?
67
+ @publishers_pri.value.positive? || @publishers_sec.value.positive?
63
68
  end
64
69
 
65
- def dispatch_push_shutdown
66
- @config.logger.debug('Dispatching push shutdown')
67
- @on[:push_shutdown].call
70
+ def dispatch_action(action)
71
+ @config.logger.debug("Dispatching action: #{action}")
72
+ @on[:action].call(action)
68
73
  end
69
74
  end
70
75
  end
@@ -13,11 +13,10 @@ module SplitIoClient
13
13
  @notification_processor = SplitIoClient::SSE::NotificationProcessor.new(config, @splits_worker, @segments_worker)
14
14
  @sse_client = SSE::EventSource::Client.new(@config) do |client|
15
15
  client.on_event { |event| handle_incoming_message(event) }
16
- client.on_connected { process_connected }
17
- client.on_disconnect { |reconnect| process_disconnect(reconnect) }
16
+ client.on_action { |action| process_action(action) }
18
17
  end
19
18
 
20
- @on = { connected: ->(_) {}, disconnect: ->(_) {} }
19
+ @on = { action: ->(_) {} }
21
20
 
22
21
  yield self if block_given?
23
22
  end
@@ -48,22 +47,14 @@ module SplitIoClient
48
47
  @segments_worker.stop
49
48
  end
50
49
 
51
- def on_connected(&action)
52
- @on[:connected] = action
53
- end
54
-
55
- def on_disconnect(&action)
56
- @on[:disconnect] = action
57
- end
58
-
59
- def process_disconnect(reconnect)
60
- @on[:disconnect].call(reconnect)
50
+ def on_action(&action)
51
+ @on[:action] = action
61
52
  end
62
53
 
63
54
  private
64
55
 
65
- def process_connected
66
- @on[:connected].call
56
+ def process_action(action)
57
+ @on[:action].call(action)
67
58
  end
68
59
 
69
60
  def handle_incoming_message(notification)
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '7.2.2'
2
+ VERSION = '7.2.3.pre.rc1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.2
4
+ version: 7.2.3.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-22 00:00:00.000000000 Z
11
+ date: 2021-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: allocation_stats
@@ -455,9 +455,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
455
455
  version: '0'
456
456
  required_rubygems_version: !ruby/object:Gem::Requirement
457
457
  requirements:
458
- - - ">="
458
+ - - ">"
459
459
  - !ruby/object:Gem::Version
460
- version: '0'
460
+ version: 1.3.1
461
461
  requirements: []
462
462
  rubygems_version: 3.0.6
463
463
  signing_key: