splitclient-rb 8.11.0.pre.rc2-java → 8.11.1-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/CHANGES.txt +10 -0
- data/LICENSE +1 -1
- data/lib/splitclient-rb/engine/events/events_manager.rb +9 -11
- data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +1 -1
- data/lib/splitclient-rb/engine/push_manager.rb +12 -8
- data/lib/splitclient-rb/engine/sync_manager.rb +8 -9
- data/lib/splitclient-rb/engine/synchronizer.rb +2 -2
- data/lib/splitclient-rb/sse/event_source/client.rb +21 -27
- data/lib/splitclient-rb/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42f4513d606ae1bd73cac99c13e599ee584143fc939d099761654718b45646ce
|
|
4
|
+
data.tar.gz: 429ed13faae4bbe470e75ecf6c03d8a03dd684eb96571d8d92c86d59f5e1ab4d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50a4fd241f140432f70e56addfe105adeac5a678bbe8f1448a9bd49764de9aba7dd623ea7df473f36d7beccdf0dcd889c081c416360f5d91ccaa28fc772bdff1
|
|
7
|
+
data.tar.gz: 2726e5c96355472ab3e7f6ec035c43dca55d9f22d6f6db026715c252769fcd02da7ad282797d61aaed69fa08b17ad33c82ddc659d9243bc08a2987caae9bdf33
|
data/CHANGES.txt
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
CHANGES
|
|
2
2
|
|
|
3
|
+
8.11.1 (Jul, 23, 2026)
|
|
4
|
+
- Added a retry mechanism in case the initial SSE socket connection failed, and extended the list of retryable errors when socket exceptions are caught during streaming.
|
|
5
|
+
- Fixed debug logging in rule-based segment matcher class when debug_enabled parameter is not set in configuration .
|
|
6
|
+
- Fixed a debug log that caused exception when streaming socket closes.
|
|
7
|
+
|
|
8
|
+
8.11.0 (Mar, 12, 2026)
|
|
9
|
+
- Added the ability to listen to different events triggered by the SDK. Read more in our docs.
|
|
10
|
+
- SDK_UPDATE notify when a flag or user segment has changed
|
|
11
|
+
- SDK_READY notify when the SDK is ready to evaluate
|
|
12
|
+
|
|
3
13
|
8.10.1 (Jan 28, 2025)
|
|
4
14
|
- Fixed rule-based segment matcher to exit when a conition is met.
|
|
5
15
|
- Fixed impressions properties format in redis mode.
|
data/LICENSE
CHANGED
|
@@ -157,7 +157,7 @@ Apache License
|
|
|
157
157
|
file or class name and description of purpose be included on the
|
|
158
158
|
same "printed page" as the copyright notice for easier
|
|
159
159
|
identification within third-party archives.
|
|
160
|
-
Copyright
|
|
160
|
+
Copyright 2025 Harness Corporation
|
|
161
161
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
162
162
|
you may not use this file except in compliance with the License.
|
|
163
163
|
You may obtain a copy of the License at
|
|
@@ -4,8 +4,6 @@ module SplitIoClient
|
|
|
4
4
|
module Engine
|
|
5
5
|
module Events
|
|
6
6
|
class EventsManager
|
|
7
|
-
include SplitIoClient::Engine::Models
|
|
8
|
-
|
|
9
7
|
def initialize(events_manager_config, events_delivery, config)
|
|
10
8
|
@manager_config = events_manager_config
|
|
11
9
|
@events_delivery = events_delivery
|
|
@@ -20,15 +18,15 @@ module SplitIoClient
|
|
|
20
18
|
|
|
21
19
|
@mutex.synchronize do
|
|
22
20
|
# SDK ready already fired
|
|
23
|
-
if sdk_event == SdkEvent::SDK_READY && event_already_triggered(sdk_event)
|
|
24
|
-
@active_subscriptions[sdk_event] = EventActiveSubscriptions.new(true, event_handler)
|
|
21
|
+
if sdk_event == Engine::Models::SdkEvent::SDK_READY && event_already_triggered(sdk_event)
|
|
22
|
+
@active_subscriptions[sdk_event] = Engine::Models::EventActiveSubscriptions.new(true, event_handler)
|
|
25
23
|
@config.logger.debug('EventsManager: Firing SDK_READY event for new subscription') if @config.debug_enabled
|
|
26
24
|
fire_sdk_event(sdk_event, nil)
|
|
27
25
|
return
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
@config.logger.debug("EventsManager: Register event: #{sdk_event}") if @config.debug_enabled
|
|
31
|
-
@active_subscriptions[sdk_event] = EventActiveSubscriptions.new(false, event_handler)
|
|
29
|
+
@active_subscriptions[sdk_event] = Engine::Models::EventActiveSubscriptions.new(false, event_handler)
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
|
|
@@ -52,7 +50,7 @@ module SplitIoClient
|
|
|
52
50
|
# if client is not subscribed to SDK_READY
|
|
53
51
|
if check_if_register_needed(sorted_event)
|
|
54
52
|
@config.logger.debug('EventsManager: Registering SDK_READY event as fired') if @config.debug_enabled
|
|
55
|
-
@active_subscriptions[SdkEvent::SDK_READY] = EventActiveSubscriptions.new(true, nil)
|
|
53
|
+
@active_subscriptions[Engine::Models::SdkEvent::SDK_READY] = Engine::Models::EventActiveSubscriptions.new(true, nil)
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
end
|
|
@@ -68,7 +66,7 @@ module SplitIoClient
|
|
|
68
66
|
private
|
|
69
67
|
|
|
70
68
|
def check_if_register_needed(sorted_event)
|
|
71
|
-
sorted_event == SdkEvent::SDK_READY &&
|
|
69
|
+
sorted_event == Engine::Models::SdkEvent::SDK_READY &&
|
|
72
70
|
get_event_handler(sorted_event).nil? &&
|
|
73
71
|
!@active_subscriptions.include?(sorted_event)
|
|
74
72
|
end
|
|
@@ -112,7 +110,7 @@ module SplitIoClient
|
|
|
112
110
|
end
|
|
113
111
|
|
|
114
112
|
def get_sdk_event_if_applicable(sdk_internal_event)
|
|
115
|
-
final_sdk_event = ValidSdkEvent.new(nil, false)
|
|
113
|
+
final_sdk_event = Engine::Models::ValidSdkEvent.new(nil, false)
|
|
116
114
|
|
|
117
115
|
events_to_fire = []
|
|
118
116
|
require_any_sdk_event = check_require_any(sdk_internal_event)
|
|
@@ -120,7 +118,7 @@ module SplitIoClient
|
|
|
120
118
|
if (!event_already_triggered(require_any_sdk_event.sdk_event) &&
|
|
121
119
|
execution_limit(require_any_sdk_event.sdk_event) == 1) ||
|
|
122
120
|
execution_limit(require_any_sdk_event.sdk_event) == -1
|
|
123
|
-
final_sdk_event = ValidSdkEvent.new(
|
|
121
|
+
final_sdk_event = Engine::Models::ValidSdkEvent.new(
|
|
124
122
|
require_any_sdk_event.sdk_event,
|
|
125
123
|
check_prerequisites(require_any_sdk_event.sdk_event) &&
|
|
126
124
|
check_suppressed_by(require_any_sdk_event.sdk_event)
|
|
@@ -180,10 +178,10 @@ module SplitIoClient
|
|
|
180
178
|
end
|
|
181
179
|
|
|
182
180
|
def check_require_any(sdk_internal_event)
|
|
183
|
-
valid_sdk_event = ValidSdkEvent.new(nil, false)
|
|
181
|
+
valid_sdk_event = Engine::Models::ValidSdkEvent.new(nil, false)
|
|
184
182
|
@manager_config.require_any.each do |name, val|
|
|
185
183
|
if val.include?(sdk_internal_event)
|
|
186
|
-
valid_sdk_event = ValidSdkEvent.new(name, true)
|
|
184
|
+
valid_sdk_event = Engine::Models::ValidSdkEvent.new(name, true)
|
|
187
185
|
return valid_sdk_event
|
|
188
186
|
end
|
|
189
187
|
end
|
|
@@ -40,7 +40,7 @@ module SplitIoClient
|
|
|
40
40
|
break if matched
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
@logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matched}")
|
|
43
|
+
@config.logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matched}") if @config.debug_enabled
|
|
44
44
|
matched
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -16,17 +16,21 @@ module SplitIoClient
|
|
|
16
16
|
response = @auth_api_client.authenticate(@api_key)
|
|
17
17
|
@config.logger.debug("Auth service response push_enabled: #{response[:push_enabled]}") if @config.debug_enabled
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
schedule_next_token_refresh(response[:
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
unless response[:push_enabled]
|
|
20
|
+
schedule_next_token_refresh(@back_off.interval) if response[:retry]
|
|
21
|
+
return false
|
|
22
|
+
end
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
unless @sse_handler.start(response[:token], response[:channels])
|
|
25
|
+
@config.logger.debug('Streaming server returned error') if @config.debug_enabled
|
|
26
|
+
stop_sse
|
|
27
|
+
return false
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
schedule_next_token_refresh(response[:exp])
|
|
31
|
+
@back_off.reset
|
|
32
|
+
record_telemetry(response[:exp])
|
|
33
|
+
true
|
|
30
34
|
rescue StandardError => e
|
|
31
35
|
@config.logger.error("start_sse: #{e.inspect}")
|
|
32
36
|
end
|
|
@@ -6,14 +6,8 @@ module SplitIoClient
|
|
|
6
6
|
SYNC_MODE_STREAMING = 0
|
|
7
7
|
SYNC_MODE_POLLING = 1
|
|
8
8
|
|
|
9
|
-
def initialize(config,
|
|
10
|
-
|
|
11
|
-
telemetry_runtime_producer,
|
|
12
|
-
telemetry_synchronizer,
|
|
13
|
-
status_manager,
|
|
14
|
-
sse_handler,
|
|
15
|
-
push_manager,
|
|
16
|
-
status_queue)
|
|
9
|
+
def initialize(config, synchronizer, telemetry_runtime_producer, telemetry_synchronizer,
|
|
10
|
+
status_manager, sse_handler, push_manager, status_queue)
|
|
17
11
|
@config = config
|
|
18
12
|
@synchronizer = synchronizer
|
|
19
13
|
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
@@ -23,6 +17,7 @@ module SplitIoClient
|
|
|
23
17
|
@push_manager = push_manager
|
|
24
18
|
@status_queue = status_queue
|
|
25
19
|
@sse_connected = Concurrent::AtomicBoolean.new(false)
|
|
20
|
+
@back_off = Engine::BackOff.new(1, 3)
|
|
26
21
|
end
|
|
27
22
|
|
|
28
23
|
def start
|
|
@@ -119,7 +114,7 @@ module SplitIoClient
|
|
|
119
114
|
end
|
|
120
115
|
|
|
121
116
|
def process_disconnect(reconnect)
|
|
122
|
-
unless @sse_connected.value
|
|
117
|
+
unless @sse_connected.value || reconnect
|
|
123
118
|
@config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
|
|
124
119
|
return
|
|
125
120
|
end
|
|
@@ -130,6 +125,9 @@ module SplitIoClient
|
|
|
130
125
|
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
131
126
|
|
|
132
127
|
if reconnect
|
|
128
|
+
wait_interval = @back_off.interval
|
|
129
|
+
@config.logger.debug("Retrying streaming connection in: #{wait_interval} seconds")
|
|
130
|
+
sleep(wait_interval)
|
|
133
131
|
@push_manager.stop_sse
|
|
134
132
|
@synchronizer.sync_all
|
|
135
133
|
@push_manager.start_sse
|
|
@@ -155,6 +153,7 @@ module SplitIoClient
|
|
|
155
153
|
|
|
156
154
|
case status
|
|
157
155
|
when Constants::PUSH_CONNECTED
|
|
156
|
+
@back_off.reset
|
|
158
157
|
process_connected
|
|
159
158
|
when Constants::PUSH_RETRYABLE_ERROR
|
|
160
159
|
process_disconnect(true)
|
|
@@ -55,8 +55,8 @@ module SplitIoClient
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def start_periodic_fetch
|
|
58
|
-
@split_fetcher.call
|
|
59
|
-
@segment_fetcher.call
|
|
58
|
+
@split_fetcher.call unless Helpers::ThreadHelper.alive?(:split_fetcher, @config)
|
|
59
|
+
@segment_fetcher.call unless Helpers::ThreadHelper.alive?(:segment_fetcher, @config)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def stop_periodic_fetch
|
|
@@ -10,7 +10,7 @@ module SplitIoClient
|
|
|
10
10
|
module EventSource
|
|
11
11
|
class Client
|
|
12
12
|
DEFAULT_READ_TIMEOUT = 70
|
|
13
|
-
CONNECT_TIMEOUT =
|
|
13
|
+
CONNECT_TIMEOUT = 30
|
|
14
14
|
OK_CODE = 200
|
|
15
15
|
KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
|
|
16
16
|
ERROR_EVENT_TYPE = 'error'.freeze
|
|
@@ -37,17 +37,15 @@ module SplitIoClient
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def close(status = nil)
|
|
40
|
-
|
|
41
|
-
@config.logger.debug('SSEClient already disconected.') if @config.debug_enabled
|
|
42
|
-
return
|
|
43
|
-
end
|
|
44
|
-
@config.logger.debug("Closing SSEClient socket") if @config.debug_enabled
|
|
40
|
+
return if @socket.nil?
|
|
45
41
|
|
|
42
|
+
@config.logger.debug("Closing SSEClient socket") if @config.debug_enabled
|
|
46
43
|
push_status(status)
|
|
47
44
|
@connected.make_false
|
|
48
45
|
@socket.sync_close = true if @socket.is_a? OpenSSL::SSL::SSLSocket
|
|
49
46
|
@socket.close
|
|
50
|
-
@config.logger.debug("SSEClient socket state #{@socket.state}") if @socket.is_a?
|
|
47
|
+
@config.logger.debug("SSEClient socket state #{@socket.state}") if @socket.is_a?(OpenSSL::SSL::SSLSocket) && @config.debug_enabled
|
|
48
|
+
@socket = nil
|
|
51
49
|
rescue StandardError => e
|
|
52
50
|
@config.logger.error("SSEClient close Error: #{e.inspect}")
|
|
53
51
|
end
|
|
@@ -80,21 +78,21 @@ module SplitIoClient
|
|
|
80
78
|
@config.threads[:connect_stream] = Thread.new do
|
|
81
79
|
@config.logger.info('Starting connect_stream thread ...')
|
|
82
80
|
new_status = connect_stream(latch)
|
|
83
|
-
push_status(new_status)
|
|
81
|
+
push_status(new_status) unless new_status.nil?
|
|
84
82
|
@config.logger.info('connect_stream thread finished.')
|
|
85
83
|
end
|
|
86
84
|
end
|
|
87
85
|
|
|
88
86
|
def connect_stream(latch)
|
|
89
|
-
return Constants::
|
|
87
|
+
return Constants::PUSH_RETRYABLE_ERROR unless socket_write(latch)
|
|
90
88
|
while connected? || @first_event.value
|
|
91
89
|
begin
|
|
92
90
|
if IO.select([@socket], nil, nil, @read_timeout)
|
|
93
91
|
begin
|
|
94
92
|
partial_data = @socket.readpartial(10_000)
|
|
95
|
-
read_first_event(partial_data, latch)
|
|
96
93
|
|
|
97
|
-
|
|
94
|
+
first_event_status = read_first_event(partial_data, latch)
|
|
95
|
+
return first_event_status unless first_event_status.nil?
|
|
98
96
|
rescue IO::WaitReadable => e
|
|
99
97
|
@config.logger.debug("SSE client IO::WaitReadable transient error: #{e.inspect}") if @config.debug_enabled
|
|
100
98
|
IO.select([@socket], nil, nil, @read_timeout)
|
|
@@ -107,8 +105,8 @@ module SplitIoClient
|
|
|
107
105
|
@config.logger.error("SSE read operation timed out!: #{e.inspect}")
|
|
108
106
|
return Constants::PUSH_RETRYABLE_ERROR
|
|
109
107
|
rescue EOFError => e
|
|
110
|
-
@config.logger.error("SSE read operation EOF
|
|
111
|
-
|
|
108
|
+
@config.logger.error("SSE read operation EOF, server closed the connection, will reconnect: #{e.inspect}")
|
|
109
|
+
return Constants::PUSH_RETRYABLE_ERROR
|
|
112
110
|
rescue Errno::EBADF, IOError => e
|
|
113
111
|
@config.logger.error("SSE read operation EBADF or IOError: #{e.inspect}")
|
|
114
112
|
return Constants::PUSH_RETRYABLE_ERROR
|
|
@@ -123,14 +121,9 @@ module SplitIoClient
|
|
|
123
121
|
@config.logger.error("SSE read operation timed out, no data available.")
|
|
124
122
|
return Constants::PUSH_RETRYABLE_ERROR
|
|
125
123
|
end
|
|
126
|
-
rescue Errno::EBADF
|
|
127
|
-
@config.logger.debug("SSE socket is not connected (Errno::EBADF)") if @config.debug_enabled
|
|
128
|
-
break
|
|
129
|
-
rescue RuntimeError
|
|
130
|
-
raise 'eof exception'
|
|
131
124
|
rescue Exception => e
|
|
132
125
|
@config.logger.debug("SSE socket is not connected: #{e.inspect}") if @config.debug_enabled
|
|
133
|
-
|
|
126
|
+
return Constants::PUSH_RETRYABLE_ERROR
|
|
134
127
|
end
|
|
135
128
|
|
|
136
129
|
process_data(partial_data)
|
|
@@ -157,19 +150,20 @@ module SplitIoClient
|
|
|
157
150
|
response_code = @event_parser.first_event(data)
|
|
158
151
|
@config.logger.debug("SSE client first event code: #{response_code}") if @config.debug_enabled
|
|
159
152
|
|
|
160
|
-
error_event = false
|
|
161
|
-
events = @event_parser.parse(data)
|
|
162
|
-
events.each { |e| error_event = true if e.event_type == ERROR_EVENT_TYPE }
|
|
163
153
|
@first_event.make_false
|
|
164
154
|
|
|
165
|
-
if response_code
|
|
166
|
-
@
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
push_status(Constants::PUSH_CONNECTED)
|
|
155
|
+
if response_code != OK_CODE
|
|
156
|
+
@config.logger.error("SSE first event failed, code: #{response_code}")
|
|
157
|
+
latch.count_down
|
|
158
|
+
return Constants::PUSH_RETRYABLE_ERROR
|
|
170
159
|
end
|
|
171
160
|
|
|
161
|
+
@connected.make_true
|
|
162
|
+
@config.logger.debug("SSE client first event Connected is true") if @config.debug_enabled
|
|
163
|
+
@telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::SSE_CONNECTION_ESTABLISHED, nil)
|
|
164
|
+
push_status(Constants::PUSH_CONNECTED)
|
|
172
165
|
latch.count_down
|
|
166
|
+
return nil
|
|
173
167
|
end
|
|
174
168
|
|
|
175
169
|
def socket_connect
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: splitclient-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.11.
|
|
4
|
+
version: 8.11.1
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Split Software
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-07-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: allocation_stats
|