splitclient-rb 7.2.0.pre.rc1-java → 7.2.3.pre.rc1-java

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
  SHA1:
3
- metadata.gz: 5764d0cc133ca646667feff52d835db4e3904212
4
- data.tar.gz: 9639b14c964fd32db9b956243310ff52407c1897
3
+ metadata.gz: 82dba9aec3fee3c5432638185912f7a0a99f80e9
4
+ data.tar.gz: 3ca561bf1fbc93340182e0c8d7416b811dc6d417
5
5
  SHA512:
6
- metadata.gz: 65cc44b9910152d1b921ed528d6c6a3791e9b084382222713ff58caa54d4bb643a768bf9a4d986a991670e9e836f9bc74c38732d2884fee501003e1fa8832f99
7
- data.tar.gz: da292d84762016a2a2b3277ff7d5001af863e8dc85f5826b22d8abb45a6de25df94c09355a7fea47e35a60b4398f355d2c0d42104464980e9b6d74283011b1e8
6
+ metadata.gz: 56196cd84b90313f8061dbf0eb4cb96127c084cd363abf7935c98ad090388beafd470b97b50d451cf2c588896ff4433fd9fe2b811b20bf568afc84a40f59fe9b
7
+ data.tar.gz: 20a873e2e90614563439d2b3cad835238515bce7b768c9ec27be089e66ad6fe3a5a0704ac9b829289d485a6388afcb7e3cfd597aa96aa07813b2ff2983602c30
@@ -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:
@@ -1,5 +1,12 @@
1
1
  CHANGES
2
2
 
3
+ 7.2.2 (Dec 18, 2020)
4
+ - Fixed issue: undefined local variable or method post_impressions_count
5
+
6
+ 7.2.1 (Oct 23, 2020)
7
+ - Updated redis dependency to >= 4.2.2.
8
+ - Updated ably error handling.
9
+
3
10
  7.2.0 (Sep 25, 2020)
4
11
  - Added deduplication logic for impressions data.
5
12
  - Now there are two modes for Impressions when the SDK is in standalone mode, OPTIMIZED (default) that only ships unique impressions and DEBUG for times where you need to send ALL impressions to debug an integration.
@@ -40,32 +40,32 @@ module SplitIoClient
40
40
  @config.logger.info('Posting impressions count due to shutdown')
41
41
  end
42
42
  end
43
+ end
43
44
 
44
- def post_impressions_count
45
- @impressions_api.post_count(formatter(@impression_counter.pop_all))
46
- rescue StandardError => error
47
- @config.log_found_exception(__method__.to_s, error)
48
- end
49
-
50
- def formatter(counts)
51
- return if counts.empty?
45
+ def post_impressions_count
46
+ @impressions_api.post_count(formatter(@impression_counter.pop_all))
47
+ rescue StandardError => error
48
+ @config.log_found_exception(__method__.to_s, error)
49
+ end
52
50
 
53
- formated_counts = {pf: []}
51
+ def formatter(counts)
52
+ return if counts.empty?
54
53
 
55
- counts.each do |key, value|
56
- key_splited = key.split('::')
57
-
58
- formated_counts[:pf] << {
59
- f: key_splited[0].to_s, # feature name
60
- m: key_splited[1].to_i, # time frame
61
- rc: value # count
62
- }
63
- end
54
+ formated_counts = {pf: []}
64
55
 
65
- formated_counts
66
- rescue StandardError => error
67
- @config.log_found_exception(__method__.to_s, error)
56
+ counts.each do |key, value|
57
+ key_splited = key.split('::')
58
+
59
+ formated_counts[:pf] << {
60
+ f: key_splited[0].to_s, # feature name
61
+ m: key_splited[1].to_i, # time frame
62
+ rc: value # count
63
+ }
68
64
  end
65
+
66
+ formated_counts
67
+ rescue StandardError => error
68
+ @config.log_found_exception(__method__.to_s, error)
69
69
  end
70
70
  end
71
71
  end
@@ -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.0.pre.rc1'
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.0.pre.rc1
4
+ version: 7.2.3.pre.rc1
5
5
  platform: java
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-19 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
  requirement: !ruby/object:Gem::Requirement