splitclient-rb 8.10.1.pre.rc.2-java → 8.11.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +8 -2
  3. data/LICENSE +1 -1
  4. data/NOTICE.txt +5 -0
  5. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +14 -1
  6. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +13 -2
  7. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +23 -1
  8. data/lib/splitclient-rb/clients/split_client.rb +12 -3
  9. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +1 -0
  10. data/lib/splitclient-rb/engine/api/splits.rb +2 -2
  11. data/lib/splitclient-rb/engine/auth_api_client.rb +7 -3
  12. data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
  13. data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
  14. data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
  15. data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
  16. data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
  17. data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
  18. data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
  19. data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
  20. data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
  21. data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
  22. data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
  23. data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
  24. data/lib/splitclient-rb/engine/status_manager.rb +7 -1
  25. data/lib/splitclient-rb/engine/sync_manager.rb +10 -6
  26. data/lib/splitclient-rb/split_factory.rb +20 -5
  27. data/lib/splitclient-rb/sse/event_source/client.rb +14 -15
  28. data/lib/splitclient-rb/sse/event_source/event_parser.rb +2 -2
  29. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +3 -3
  30. data/lib/splitclient-rb/sse/workers/segments_worker.rb +4 -4
  31. data/lib/splitclient-rb/sse/workers/splits_worker.rb +9 -5
  32. data/lib/splitclient-rb/version.rb +1 -1
  33. data/lib/splitclient-rb.rb +12 -0
  34. metadata +15 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff09202393ac5e890e9eaf656b9da3f5b43e4322593327eb0415a8731891e45d
4
- data.tar.gz: 4e5024549a2747345451c3d98b767b8c72c8df83c1d3f47407c716be59d06570
3
+ metadata.gz: dd598b2e9efb5540f5fb04f365b8f39cca8ded197ba8ff182a4335993f7e6d6e
4
+ data.tar.gz: '09b81db07f63a50e86450914abe2301bef42decfda218f8bce9a9077333040ba'
5
5
  SHA512:
6
- metadata.gz: 0f15c461e9b89c2583761d9c71f82e4bbba426bf79f285701cf3095207a981cd0de49be5472538b4f307b145dfb2bdd91191af995a1c778809d3c2236646273b
7
- data.tar.gz: 999209470a3834f08f7bb4403a9128d1ebbbe6f6660560a88f4c1baea6c61772c22b3860effa8d35030c76172664061f89c90c30bf681892cc8f57ba84fb5ad0
6
+ metadata.gz: 26ef8859967e564baf87ac4b045eb49eb2001faea6bc38e331a61c9df8d9ff03ad8f06dd226b677e85dc3e5d3c9b6b951b1f1f6abdc4f01f8b0d44bd8ace7f8c
7
+ data.tar.gz: baeef34eda98aefd67bd494e60f6fe7e866ea43cb17f1fa299b49800373d65e17611b979dd2fc60bd169f3c4659c6aebbc6e2dbe47c374002239e4c13111970e
data/CHANGES.txt CHANGED
@@ -1,7 +1,13 @@
1
1
  CHANGES
2
2
 
3
- 8.10.1 (Jan 21, 2025)
4
- - Fixed rule-based segment matcher.
3
+ 8.11.0 (Mar, 12, 2026)
4
+ - Added the ability to listen to different events triggered by the SDK. Read more in our docs.
5
+ - SDK_UPDATE notify when a flag or user segment has changed
6
+ - SDK_READY notify when the SDK is ready to evaluate
7
+
8
+ 8.10.1 (Jan 28, 2025)
9
+ - Fixed rule-based segment matcher to exit when a conition is met.
10
+ - Fixed impressions properties format in redis mode.
5
11
 
6
12
  8.10.0 (Nov 28, 2025)
7
13
  - Updated socketry gem used in streaming feature with built-in socket lib.
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 [yyyy] [name of copyright owner]
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
data/NOTICE.txt ADDED
@@ -0,0 +1,5 @@
1
+ Harness Feature Management JavaScript SDK Copyright 2024-2026 Harness Inc.
2
+
3
+ This product includes software developed at Harness Inc. (https://harness.io/).
4
+
5
+ This product includes software originally developed by Split Software, Inc. (https://www.split.io/). Copyright 2016-2024 Split Software, Inc.
@@ -28,7 +28,7 @@ module SplitIoClient
28
28
  RB_SEGMENTS_PREFIX = '.rbsegment.'
29
29
  REGISTERED_PREFIX = '.segments.registered'
30
30
 
31
- def initialize(config)
31
+ def initialize(config, internal_events_queue)
32
32
  super(config)
33
33
  @adapter = case @config.cache_adapter.class.to_s
34
34
  when 'SplitIoClient::Cache::Adapters::RedisAdapter'
@@ -40,12 +40,25 @@ module SplitIoClient
40
40
  @adapter.set_string(namespace_key(TILL_PREFIX), '-1')
41
41
  @adapter.initialize_map(namespace_key(REGISTERED_PREFIX))
42
42
  end
43
+ @internal_events_queue = internal_events_queue
43
44
  end
44
45
 
45
46
  def update(to_add, to_delete, new_change_number)
46
47
  to_add.each{ |rule_based_segment| add_rule_based_segment(rule_based_segment) }
47
48
  to_delete.each{ |rule_based_segment| remove_rule_based_segment(rule_based_segment) }
48
49
  set_change_number(new_change_number)
50
+
51
+ if to_add.length > 0 || to_delete.length > 0
52
+ @internal_events_queue.push(
53
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
54
+ SplitIoClient::Engine::Models::SdkInternalEvent::RB_SEGMENTS_UPDATED,
55
+ SplitIoClient::Engine::Models::EventsMetadata.new(
56
+ SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
57
+ []
58
+ )
59
+ )
60
+ )
61
+ end
49
62
  end
50
63
 
51
64
  def get_rule_based_segment(name)
@@ -6,7 +6,7 @@ module SplitIoClient
6
6
 
7
7
  attr_reader :adapter
8
8
 
9
- def initialize(config)
9
+ def initialize(config, internal_events_queue)
10
10
  super(config)
11
11
  @adapter = case @config.cache_adapter.class.to_s
12
12
  when 'SplitIoClient::Cache::Adapters::RedisAdapter'
@@ -15,6 +15,7 @@ module SplitIoClient
15
15
  @config.cache_adapter
16
16
  end
17
17
  @adapter.set_bool(namespace_key('.ready'), false) unless @config.mode.equal?(:consumer)
18
+ @internal_events_queue = internal_events_queue
18
19
  end
19
20
 
20
21
  # Receives segment data, adds and removes segements from the store
@@ -22,9 +23,19 @@ module SplitIoClient
22
23
  name = segment[:name]
23
24
 
24
25
  @adapter.initialize_set(segment_data(name)) unless @adapter.exists?(segment_data(name))
25
-
26
26
  add_keys(name, segment[:added])
27
27
  remove_keys(name, segment[:removed])
28
+ if segment[:added].length > 0 || segment[:removed].length > 0
29
+ @internal_events_queue.push(
30
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
31
+ SplitIoClient::Engine::Models::SdkInternalEvent::SEGMENTS_UPDATED,
32
+ SplitIoClient::Engine::Models::EventsMetadata.new(
33
+ SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
34
+ []
35
+ )
36
+ )
37
+ )
38
+ end
28
39
  end
29
40
 
30
41
  def get_segment_keys(name)
@@ -35,7 +35,7 @@ module SplitIoClient
35
35
  SPLIT_PREFIX = '.split.'
36
36
  READY_PREFIX = '.splits.ready'
37
37
 
38
- def initialize(config, flag_sets_repository, flag_set_filter)
38
+ def initialize(config, flag_sets_repository, flag_set_filter, internal_events_queue)
39
39
  super(config)
40
40
  @tt_cache = {}
41
41
  @adapter = case @config.cache_adapter.class.to_s
@@ -46,6 +46,7 @@ module SplitIoClient
46
46
  end
47
47
  @flag_sets = flag_sets_repository
48
48
  @flag_set_filter = flag_set_filter
49
+ @internal_events_queue = internal_events_queue
49
50
  initialize_keys
50
51
  end
51
52
 
@@ -53,6 +54,18 @@ module SplitIoClient
53
54
  to_add.each{ |feature_flag| add_feature_flag(feature_flag) }
54
55
  to_delete.each{ |feature_flag| remove_feature_flag(feature_flag) }
55
56
  set_change_number(new_change_number)
57
+
58
+ if to_add.length > 0 || to_delete.length > 0
59
+ @internal_events_queue.push(
60
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
61
+ SplitIoClient::Engine::Models::SdkInternalEvent::FLAGS_UPDATED,
62
+ SplitIoClient::Engine::Models::EventsMetadata.new(
63
+ SplitIoClient::Engine::Models::SdkEventType::FLAG_UPDATE,
64
+ to_add.map {|flag| flag[:name]} | to_delete.map {|flag| flag[:name]}
65
+ )
66
+ )
67
+ )
68
+ end
56
69
  end
57
70
 
58
71
  def get_split(name)
@@ -140,6 +153,15 @@ module SplitIoClient
140
153
  split[:changeNumber] = change_number
141
154
 
142
155
  @adapter.set_string(namespace_key(".split.#{split_name}"), split.to_json)
156
+ @internal_events_queue.push(
157
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
158
+ SplitIoClient::Engine::Models::SdkInternalEvent::FLAG_KILLED_NOTIFICATION,
159
+ SplitIoClient::Engine::Models::EventsMetadata.new(
160
+ SplitIoClient::Engine::Models::SdkEventType::FLAG_UPDATE,
161
+ [split_name]
162
+ )
163
+ )
164
+ )
143
165
  end
144
166
 
145
167
  def splits_count
@@ -18,7 +18,7 @@ module SplitIoClient
18
18
  # @param sdk_key [String] the SDK key for your split account
19
19
  #
20
20
  # @return [SplitIoClient] split.io client instance
21
- def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator)
21
+ def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator, events_manager)
22
22
  @api_key = sdk_key
23
23
  @splits_repository = repositories[:splits]
24
24
  @segments_repository = repositories[:segments]
@@ -33,6 +33,7 @@ module SplitIoClient
33
33
  @split_validator = split_validator
34
34
  @evaluator = evaluator
35
35
  @fallback_treatment_calculator = fallback_treatment_calculator
36
+ @events_manager = events_manager
36
37
  end
37
38
 
38
39
  def get_treatment(
@@ -117,11 +118,11 @@ module SplitIoClient
117
118
  @config.logger.info('Split client shutdown started...') if @config.debug_enabled
118
119
  if !@config.cache_adapter.is_a?(SplitIoClient::Cache::Adapters::RedisAdapter) && @config.impressions_mode != :none &&
119
120
  (!@impressions_repository.empty? || !@events_repository.empty?)
120
- @config.logger.debug("Impressions and/or Events cache is not empty")
121
+ @config.logger.debug("Impressions and/or Events cache is not empty") if @config.debug_enabled
121
122
  # Adding small delay to ensure sender threads are fully running
122
123
  sleep(0.1)
123
124
  if !@config.threads.key?(:impressions_sender) || !@config.threads.key?(:events_sender)
124
- @config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.")
125
+ @config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.") if @config.debug_enabled
125
126
  @config.threads[:start_sdk].join(5) if @config.threads.key?(:start_sdk)
126
127
  end
127
128
  end
@@ -176,6 +177,14 @@ module SplitIoClient
176
177
  @status_manager.wait_until_ready(time) if @status_manager
177
178
  end
178
179
 
180
+ def register(sdk_event, handler)
181
+ @events_manager.register(sdk_event, handler)
182
+ end
183
+
184
+ def unregister(sdk_event, handler)
185
+ @events_manager.unregister(sdk_event)
186
+ end
187
+
179
188
  private
180
189
 
181
190
  def check_properties_size(properties_size, msg = "Event not queued")
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday'
4
+ require 'stringio'
4
5
 
5
6
  module SplitIoClient
6
7
  module FaradayMiddleware
@@ -24,7 +24,7 @@ module SplitIoClient
24
24
 
25
25
  if check_last_proxy_check_timestamp
26
26
  @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
27
- @config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.")
27
+ @config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.") if @config.debug_enabled
28
28
  @old_spec_since = since
29
29
  since = -1
30
30
  since_rbs = -1
@@ -41,7 +41,7 @@ module SplitIoClient
41
41
 
42
42
  params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
43
43
  params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
44
- @config.logger.debug("Fetching from splitChanges with #{params}: ")
44
+ @config.logger.debug("Fetching from splitChanges with #{params}: ") if @config.debug_enabled
45
45
  response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
46
46
  if response.status == 414
47
47
  @config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
@@ -21,10 +21,12 @@ module SplitIoClient
21
21
  return process_error(response) if response.status >= 400 && response.status < 500
22
22
 
23
23
  @telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::TOKEN_SYNC, response.status.to_i)
24
- @config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
24
+ if @config.debug_enabled
25
+ @config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
26
+ end
25
27
  { push_enabled: false, retry: true }
26
28
  rescue StandardError => e
27
- @config.logger.debug("AuthApiClient error: #{e.inspect}.")
29
+ @config.logger.debug("AuthApiClient error: #{e.inspect}.") if @config.debug_enabled
28
30
  { push_enabled: false, retry: false }
29
31
  end
30
32
 
@@ -51,7 +53,9 @@ module SplitIoClient
51
53
  end
52
54
 
53
55
  def process_error(response)
54
- @config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
56
+ if @config.debug_enabled
57
+ @config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
58
+ end
55
59
  @telemetry_runtime_producer.record_auth_rejections if response.status == 401
56
60
 
57
61
  { push_enabled: false, retry: false }
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class EventsDelivery
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def deliver(sdk_event, event_metadata, event_handler)
12
+ event_handler.call(event_metadata)
13
+ rescue StandardError => e
14
+ @config.logger.error("Exception when calling handler for Sdk Event #{sdk_event}")
15
+ @config.log_found_exception(__method__.to_s, e)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class EventsManager
7
+ def initialize(events_manager_config, events_delivery, config)
8
+ @manager_config = events_manager_config
9
+ @events_delivery = events_delivery
10
+ @active_subscriptions = {}
11
+ @internal_events_status = {}
12
+ @mutex = Mutex.new
13
+ @config = config
14
+ end
15
+
16
+ def register(sdk_event, event_handler)
17
+ return if @active_subscriptions.key?(sdk_event) && !get_event_handler(sdk_event).nil?
18
+
19
+ @mutex.synchronize do
20
+ # SDK ready already fired
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)
23
+ @config.logger.debug('EventsManager: Firing SDK_READY event for new subscription') if @config.debug_enabled
24
+ fire_sdk_event(sdk_event, nil)
25
+ return
26
+ end
27
+
28
+ @config.logger.debug("EventsManager: Register event: #{sdk_event}") if @config.debug_enabled
29
+ @active_subscriptions[sdk_event] = Engine::Models::EventActiveSubscriptions.new(false, event_handler)
30
+ end
31
+ end
32
+
33
+ def unregister(sdk_event)
34
+ return unless @active_subscriptions.key?(sdk_event)
35
+
36
+ @mutex.synchronize do
37
+ @active_subscriptions.delete(sdk_event)
38
+ end
39
+ end
40
+
41
+ def notify_internal_event(sdk_internal_event, event_metadata)
42
+ @mutex.synchronize do
43
+ update_internal_event_status(sdk_internal_event, true)
44
+ @manager_config.evaluation_order.each do |sorted_event|
45
+ if get_sdk_event_if_applicable(sdk_internal_event).include?(sorted_event) &&
46
+ !get_event_handler(sorted_event).nil?
47
+ fire_sdk_event(sorted_event, event_metadata)
48
+ end
49
+
50
+ # if client is not subscribed to SDK_READY
51
+ if check_if_register_needed(sorted_event)
52
+ @config.logger.debug('EventsManager: Registering SDK_READY event as fired') if @config.debug_enabled
53
+ @active_subscriptions[Engine::Models::SdkEvent::SDK_READY] = Engine::Models::EventActiveSubscriptions.new(true, nil)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def destroy
60
+ @mutex.synchronize do
61
+ @active_subscriptions = {}
62
+ @internal_events_status = {}
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def check_if_register_needed(sorted_event)
69
+ sorted_event == Engine::Models::SdkEvent::SDK_READY &&
70
+ get_event_handler(sorted_event).nil? &&
71
+ !@active_subscriptions.include?(sorted_event)
72
+ end
73
+
74
+ def fire_sdk_event(sdk_event, event_metadata)
75
+ @config.logger.debug("EventsManager: Firing Sdk event: #{sdk_event}") if @config.debug_enabled
76
+ @config.threads[:sdk_event_notify] = Thread.new do
77
+ @events_delivery.deliver(sdk_event, event_metadata, get_event_handler(sdk_event))
78
+ end
79
+ sdk_event_triggered(sdk_event)
80
+ end
81
+
82
+ def event_already_triggered(sdk_event)
83
+ return @active_subscriptions[sdk_event].triggered if @active_subscriptions.key?(sdk_event)
84
+
85
+ false
86
+ end
87
+
88
+ def get_internal_event_status(sdk_internal_event)
89
+ return @internal_events_status[sdk_internal_event] if @internal_events_status.key?(sdk_internal_event)
90
+
91
+ false
92
+ end
93
+
94
+ def update_internal_event_status(sdk_internal_event, status)
95
+ @internal_events_status[sdk_internal_event] = status
96
+ end
97
+
98
+ def sdk_event_triggered(sdk_event)
99
+ return unless @active_subscriptions.key?(sdk_event)
100
+
101
+ return if @active_subscriptions[sdk_event].triggered
102
+
103
+ @active_subscriptions[sdk_event].triggered = true
104
+ end
105
+
106
+ def get_event_handler(sdk_event)
107
+ return nil unless @active_subscriptions.key?(sdk_event)
108
+
109
+ @active_subscriptions[sdk_event].handler
110
+ end
111
+
112
+ def get_sdk_event_if_applicable(sdk_internal_event)
113
+ final_sdk_event = Engine::Models::ValidSdkEvent.new(nil, false)
114
+
115
+ events_to_fire = []
116
+ require_any_sdk_event = check_require_any(sdk_internal_event)
117
+ if require_any_sdk_event.valid
118
+ if (!event_already_triggered(require_any_sdk_event.sdk_event) &&
119
+ execution_limit(require_any_sdk_event.sdk_event) == 1) ||
120
+ execution_limit(require_any_sdk_event.sdk_event) == -1
121
+ final_sdk_event = Engine::Models::ValidSdkEvent.new(
122
+ require_any_sdk_event.sdk_event,
123
+ check_prerequisites(require_any_sdk_event.sdk_event) &&
124
+ check_suppressed_by(require_any_sdk_event.sdk_event)
125
+ )
126
+ end
127
+ events_to_fire.push(final_sdk_event.sdk_event) if final_sdk_event.valid
128
+ end
129
+ check_require_all.each { |sdk_event| events_to_fire.push(sdk_event) }
130
+
131
+ events_to_fire
132
+ end
133
+
134
+ def check_require_all
135
+ events = []
136
+ @manager_config.require_all.each do |require_name, require_value|
137
+ final_status = true
138
+ require_value.each { |val| final_status &= get_internal_event_status(val) }
139
+ events.push(require_name) if check_event_eligible_conditions(final_status, require_name, require_value)
140
+ end
141
+
142
+ events
143
+ end
144
+
145
+ def check_event_eligible_conditions(final_status, require_name, require_value)
146
+ final_status &&
147
+ check_prerequisites(require_name) &&
148
+ ((!event_already_triggered(require_name) &&
149
+ execution_limit(require_name) == 1) ||
150
+ execution_limit(require_name) == -1) &&
151
+ require_value.length.positive?
152
+ end
153
+
154
+ def check_prerequisites(sdk_event)
155
+ @manager_config.prerequisites.each do |name, value|
156
+ value.each do |val|
157
+ return false if name == sdk_event && !event_already_triggered(val)
158
+ end
159
+ end
160
+
161
+ true
162
+ end
163
+
164
+ def check_suppressed_by(sdk_event)
165
+ @manager_config.suppressed_by.each do |name, value|
166
+ value.each do |val|
167
+ return false if name == sdk_event && event_already_triggered(val)
168
+ end
169
+ end
170
+
171
+ true
172
+ end
173
+
174
+ def execution_limit(sdk_event)
175
+ return -1 unless @manager_config.execution_limits.key?(sdk_event)
176
+
177
+ @manager_config.execution_limits[sdk_event]
178
+ end
179
+
180
+ def check_require_any(sdk_internal_event)
181
+ valid_sdk_event = Engine::Models::ValidSdkEvent.new(nil, false)
182
+ @manager_config.require_any.each do |name, val|
183
+ if val.include?(sdk_internal_event)
184
+ valid_sdk_event = Engine::Models::ValidSdkEvent.new(name, true)
185
+ return valid_sdk_event
186
+ end
187
+ end
188
+
189
+ valid_sdk_event
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class EventsManagerConfig
7
+ attr_accessor :require_all, :prerequisites, :require_any, :suppressed_by, :execution_limits, :evaluation_order
8
+
9
+ def initialize
10
+ @require_all = construct_require_all
11
+ @prerequisites = construct_prerequisites
12
+ @require_any = construct_require_any
13
+ @suppressed_by = construct_suppressed_by
14
+ @execution_limits = construct_execution_limits
15
+ @evaluation_order = construct_sorted_events
16
+ end
17
+
18
+ private
19
+
20
+ def construct_require_all
21
+ {
22
+ SplitIoClient::Engine::Models::SdkEvent::SDK_READY => Set.new([SplitIoClient::Engine::Models::SdkInternalEvent::SDK_READY])
23
+ }
24
+ end
25
+
26
+ def construct_prerequisites
27
+ {
28
+ SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => Set.new([SplitIoClient::Engine::Models::SdkEvent::SDK_READY])
29
+ }
30
+ end
31
+
32
+ def construct_require_any
33
+ {
34
+ SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => Set.new(
35
+ [
36
+ SplitIoClient::Engine::Models::SdkInternalEvent::FLAG_KILLED_NOTIFICATION,
37
+ SplitIoClient::Engine::Models::SdkInternalEvent::FLAGS_UPDATED,
38
+ SplitIoClient::Engine::Models::SdkInternalEvent::RB_SEGMENTS_UPDATED,
39
+ SplitIoClient::Engine::Models::SdkInternalEvent::SEGMENTS_UPDATED
40
+ ]
41
+ )
42
+ }
43
+ end
44
+
45
+ def construct_suppressed_by
46
+ {}
47
+ end
48
+
49
+ def construct_execution_limits
50
+ {
51
+ SplitIoClient::Engine::Models::SdkEvent::SDK_READY => 1,
52
+ SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => -1
53
+ }
54
+ end
55
+
56
+ def construct_sorted_events
57
+ sorted_events = []
58
+ [SplitIoClient::Engine::Models::SdkEvent::SDK_READY, SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE].each do |sdk_event|
59
+ sorted_events = dfs_recursive(sdk_event, sorted_events)
60
+ end
61
+
62
+ sorted_events
63
+ end
64
+
65
+ def dfs_recursive(sdk_event, added)
66
+ return added if added.include?(sdk_event)
67
+
68
+ get_dependencies(sdk_event).each do |dependent_event|
69
+ added = dfs_recursive(dependent_event, added)
70
+ end
71
+
72
+ added.push(sdk_event)
73
+
74
+ added
75
+ end
76
+
77
+ def get_dependencies(sdk_event)
78
+ dependencies = Set.new
79
+ @prerequisites.each do |prerequisites_event_name, prerequisites_event_value|
80
+ next unless prerequisites_event_name == sdk_event
81
+
82
+ prerequisites_event_value.each do |prereq_event|
83
+ dependencies.add(prereq_event)
84
+ end
85
+ end
86
+
87
+ @suppressed_by.each do |suppressed_event_name, suppressed_event_value|
88
+ dependencies.add(suppressed_event_name) if suppressed_event_value.include?(sdk_event)
89
+ end
90
+
91
+ dependencies
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class EventsTask
7
+ attr_accessor :running
8
+
9
+ def initialize(notify_internal_events, internal_events_queue, config)
10
+ @notify_internal_events = notify_internal_events
11
+ @internal_events_queue = internal_events_queue
12
+ @config = config
13
+ @running = false
14
+ end
15
+
16
+ def start
17
+ return if @running
18
+
19
+ @config.logger.info('Starting Internal Events Task.')
20
+ @running = true
21
+ @config.threads[:internal_events_task] = Thread.new do
22
+ worker_thread
23
+ end
24
+ end
25
+
26
+ def stop
27
+ return unless @running
28
+
29
+ @config.logger.info('Stopping Internal Events Task.')
30
+ @running = false
31
+ end
32
+
33
+ private
34
+
35
+ def worker_thread
36
+ while (event = @internal_events_queue.pop)
37
+ break unless @running
38
+
39
+ @config.logger.debug("Processing sdk internal event: #{event.internal_event}") if @config.debug_enabled
40
+ begin
41
+ @notify_internal_events.call(event.internal_event, event.metadata)
42
+ rescue StandardError => e
43
+ @config.log_found_exception(__method__.to_s, e)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Events
6
+ class NoOpEventsQueue
7
+ def push(sdk_event)
8
+ # do nothing
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module Engine::Models
5
+ class EventActiveSubscriptions
6
+ attr_accessor :triggered, :handler
7
+
8
+ def initialize(triggered, handler)
9
+ @triggered = triggered
10
+ @handler = handler
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module SplitIoClient::Engine::Models
2
+ class EventsMetadata
3
+ attr_accessor :type, :names
4
+
5
+ def initialize(type, names=nil)
6
+ @type = type
7
+ @names = names
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ class SplitIoClient::Engine::Models::SdkEvent
2
+ SDK_READY = 'SDK_READY'
3
+ SDK_UPDATE = 'SDK_UPDATE'
4
+ end
@@ -0,0 +1,4 @@
1
+ class SplitIoClient::Engine::Models::SdkEventType
2
+ FLAG_UPDATE = 'FLAG_UPDATE'
3
+ SEGMENTS_UPDATE = 'SEGMENTS_UPDATE'
4
+ end
@@ -0,0 +1,8 @@
1
+ class SplitIoClient::Engine::Models::SdkInternalEvent
2
+ SDK_READY = 'SDK_READY'
3
+ FLAGS_UPDATED = 'FLAGS_UPDATED'
4
+ FLAG_KILLED_NOTIFICATION = 'FLAG_KILLED_NOTIFICATION'
5
+ SEGMENTS_UPDATED = 'SEGMENTS_UPDATED'
6
+ RB_SEGMENTS_UPDATED = 'RB_SEGMENTS_UPDATED'
7
+ LARGE_SEGMENTS_UPDATED = 'LARGE_SEGMENTS_UPDATED'
8
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ module Models
6
+ class SdkInternalEventNotification
7
+ attr_reader :internal_event, :metadata
8
+
9
+ def initialize(internal_event, metadata)
10
+ @internal_event = internal_event
11
+ @metadata = metadata
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module Engine::Models
5
+ class ValidSdkEvent
6
+ attr_reader :sdk_event, :valid
7
+
8
+ def initialize(sdk_event, valid)
9
+ @sdk_event = sdk_event
10
+ @valid = valid
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,9 +3,10 @@
3
3
  module SplitIoClient
4
4
  module Engine
5
5
  class StatusManager
6
- def initialize(config)
6
+ def initialize(config, internal_events_queue)
7
7
  @config = config
8
8
  @sdk_ready = Concurrent::CountDownLatch.new(1)
9
+ @internal_events_queue = internal_events_queue
9
10
  end
10
11
 
11
12
  def ready?
@@ -19,6 +20,11 @@ module SplitIoClient
19
20
 
20
21
  @sdk_ready.count_down
21
22
  @config.logger.info('SplitIO SDK is ready')
23
+ @internal_events_queue.push(
24
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
25
+ SplitIoClient::Engine::Models::SdkInternalEvent::SDK_READY, nil
26
+ )
27
+ )
22
28
  end
23
29
 
24
30
  def wait_until_ready(seconds = nil)
@@ -47,13 +47,13 @@ module SplitIoClient
47
47
  connected = false
48
48
 
49
49
  if @config.streaming_enabled
50
- @config.logger.debug('Starting Streaming mode ...')
50
+ @config.logger.debug('Starting Streaming mode ...') if @config.debug_enabled
51
51
  start_push_status_monitor
52
52
  connected = @push_manager.start_sse
53
53
  end
54
54
 
55
55
  unless connected
56
- @config.logger.debug('Starting Polling mode ...')
56
+ @config.logger.debug('Starting Polling mode ...') if @config.debug_enabled
57
57
  @synchronizer.start_periodic_fetch
58
58
  record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
59
59
  end
@@ -92,7 +92,7 @@ module SplitIoClient
92
92
 
93
93
  def process_connected
94
94
  if @sse_connected.value
95
- @config.logger.debug('Streaming already connected.')
95
+ @config.logger.debug('Streaming already connected.') if @config.debug_enabled
96
96
  return
97
97
  end
98
98
 
@@ -107,7 +107,7 @@ module SplitIoClient
107
107
 
108
108
  def process_forced_stop
109
109
  unless @sse_connected.value
110
- @config.logger.debug('Streaming already disconnected.')
110
+ @config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
111
111
  return
112
112
  end
113
113
 
@@ -120,7 +120,7 @@ module SplitIoClient
120
120
 
121
121
  def process_disconnect(reconnect)
122
122
  unless @sse_connected.value
123
- @config.logger.debug('Streaming already disconnected.')
123
+ @config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
124
124
  return
125
125
  end
126
126
 
@@ -169,12 +169,16 @@ module SplitIoClient
169
169
  when Constants::PUSH_SUBSYSTEM_OFF
170
170
  process_push_shutdown
171
171
  else
172
- @config.logger.debug('Incorrect push status type.')
172
+ log_if_debug('Incorrect push status type.')
173
173
  end
174
174
  end
175
175
  rescue StandardError => e
176
176
  @config.logger.error("Push status handler error: #{e.inspect}")
177
177
  end
178
178
  end
179
+
180
+ def log_if_debug(msg)
181
+ @config.logger.debug(msg) if @config.debug_enabled
182
+ end
179
183
  end
180
184
  end
@@ -45,6 +45,7 @@ module SplitIoClient
45
45
 
46
46
  register_factory
47
47
 
48
+ build_events_manager
48
49
  build_telemetry_components
49
50
  build_flag_sets_filter
50
51
  build_repositories
@@ -53,13 +54,13 @@ module SplitIoClient
53
54
  build_unique_keys_tracker
54
55
  build_impressions_components
55
56
 
56
- @status_manager = Engine::StatusManager.new(@config)
57
+ @status_manager = Engine::StatusManager.new(@config, @internal_events_queue)
57
58
  @split_validator = SplitIoClient::Validators.new(@config)
58
59
  @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @rule_based_segment_repository, @config)
59
60
 
60
61
  start!
61
62
  fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(@config.fallback_treatments_configuration)
62
- @client = SplitClient.new(@api_key, repositories, @status_manager, @config, @impressions_manager, @evaluation_producer, @evaluator, @split_validator, fallback_treatment_calculator)
63
+ @client = SplitClient.new(@api_key, repositories, @status_manager, @config, @impressions_manager, @evaluation_producer, @evaluator, @split_validator, fallback_treatment_calculator, @events_manager)
63
64
  @manager = SplitManager.new(@splits_repository, @status_manager, @config)
64
65
  end
65
66
 
@@ -219,9 +220,9 @@ module SplitIoClient
219
220
  else
220
221
  @flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new(@config.flag_sets_filter)
221
222
  end
222
- @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter)
223
- @segments_repository = SegmentsRepository.new(@config)
224
- @rule_based_segment_repository = RuleBasedSegmentsRepository.new(@config)
223
+ @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter, @internal_events_queue)
224
+ @segments_repository = SegmentsRepository.new(@config, @internal_events_queue)
225
+ @rule_based_segment_repository = RuleBasedSegmentsRepository.new(@config, @internal_events_queue)
225
226
  @impressions_repository = ImpressionsRepository.new(@config)
226
227
  @events_repository = EventsRepository.new(@config, @api_key, @runtime_producer)
227
228
  end
@@ -265,5 +266,19 @@ module SplitIoClient
265
266
  def build_flag_sets_filter
266
267
  @flag_sets_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new(@config.flag_sets_filter)
267
268
  end
269
+
270
+ def build_events_manager
271
+ @events_manager = Engine::Events::EventsManager.new(Engine::Events::EventsManagerConfig.new,
272
+ Engine::Events::EventsDelivery.new(@config),
273
+ @config)
274
+ if @config.consumer?
275
+ @internal_events_queue = Engine::Events::NoOpEventsQueue.new
276
+ return
277
+ end
278
+
279
+ @internal_events_queue = Queue.new
280
+ @events_task = Engine::Events::EventsTask.new(@events_manager.method(:notify_internal_event), @internal_events_queue, @config)
281
+ @events_task.start
282
+ end
268
283
  end
269
284
  end
@@ -38,23 +38,23 @@ module SplitIoClient
38
38
 
39
39
  def close(status = nil)
40
40
  unless connected?
41
- @config.logger.debug('SSEClient already disconected.')
41
+ @config.logger.debug('SSEClient already disconected.') if @config.debug_enabled
42
42
  return
43
43
  end
44
- @config.logger.debug("Closing SSEClient socket")
44
+ @config.logger.debug("Closing SSEClient socket") if @config.debug_enabled
45
45
 
46
+ push_status(status)
46
47
  @connected.make_false
47
48
  @socket.sync_close = true if @socket.is_a? OpenSSL::SSL::SSLSocket
48
49
  @socket.close
49
- @config.logger.debug("SSEClient socket state #{@socket.state}") if @socket.is_a? OpenSSL::SSL::SSLSocket
50
- push_status(status)
50
+ @config.logger.debug("SSEClient socket state #{@socket.state}") if @socket.is_a? OpenSSL::SSL::SSLSocket && @config.debug_enabled
51
51
  rescue StandardError => e
52
52
  @config.logger.error("SSEClient close Error: #{e.inspect}")
53
53
  end
54
54
 
55
55
  def start(url)
56
56
  if connected?
57
- @config.logger.debug('SSEClient already running.')
57
+ @config.logger.debug('SSEClient already running.') if @config.debug_enabled
58
58
  return true
59
59
  end
60
60
 
@@ -96,18 +96,17 @@ module SplitIoClient
96
96
 
97
97
  raise 'eof exception' if partial_data == :eof
98
98
  rescue IO::WaitReadable => e
99
- @config.logger.debug("SSE client IO::WaitReadable transient error: #{e.inspect}")
99
+ @config.logger.debug("SSE client IO::WaitReadable transient error: #{e.inspect}") if @config.debug_enabled
100
100
  IO.select([@socket], nil, nil, @read_timeout)
101
101
  retry
102
102
  rescue Errno::EAGAIN => e
103
- @config.logger.debug("SSE client transient error: #{e.inspect}")
103
+ @config.logger.debug("SSE client transient error: #{e.inspect}") if @config.debug_enabled
104
104
  IO.select([@socket], nil, nil, @read_timeout)
105
105
  retry
106
106
  rescue Errno::ETIMEDOUT => e
107
107
  @config.logger.error("SSE read operation timed out!: #{e.inspect}")
108
108
  return Constants::PUSH_RETRYABLE_ERROR
109
109
  rescue EOFError => e
110
- puts "SSE read operation EOF Exception!: #{e.inspect}"
111
110
  @config.logger.error("SSE read operation EOF Exception!: #{e.inspect}")
112
111
  raise 'eof exception'
113
112
  rescue Errno::EBADF, IOError => e
@@ -125,12 +124,12 @@ module SplitIoClient
125
124
  return Constants::PUSH_RETRYABLE_ERROR
126
125
  end
127
126
  rescue Errno::EBADF
128
- @config.logger.debug("SSE socket is not connected (Errno::EBADF)")
127
+ @config.logger.debug("SSE socket is not connected (Errno::EBADF)") if @config.debug_enabled
129
128
  break
130
129
  rescue RuntimeError
131
130
  raise 'eof exception'
132
131
  rescue Exception => e
133
- @config.logger.debug("SSE socket is not connected: #{e.inspect}")
132
+ @config.logger.debug("SSE socket is not connected: #{e.inspect}") if @config.debug_enabled
134
133
  break
135
134
  end
136
135
 
@@ -156,7 +155,7 @@ module SplitIoClient
156
155
  return unless @first_event.value
157
156
 
158
157
  response_code = @event_parser.first_event(data)
159
- @config.logger.debug("SSE client first event code: #{response_code}")
158
+ @config.logger.debug("SSE client first event code: #{response_code}") if @config.debug_enabled
160
159
 
161
160
  error_event = false
162
161
  events = @event_parser.parse(data)
@@ -165,7 +164,7 @@ module SplitIoClient
165
164
 
166
165
  if response_code == OK_CODE && !error_event
167
166
  @connected.make_true
168
- @config.logger.debug("SSE client first event Connected is true")
167
+ @config.logger.debug("SSE client first event Connected is true") if @config.debug_enabled
169
168
  @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::SSE_CONNECTION_ESTABLISHED, nil)
170
169
  push_status(Constants::PUSH_CONNECTED)
171
170
  end
@@ -202,7 +201,7 @@ module SplitIoClient
202
201
  end
203
202
 
204
203
  def process_data(partial_data)
205
- @config.logger.debug("Event partial data: #{partial_data}")
204
+ @config.logger.debug("Event partial data: #{partial_data}") if @config.debug_enabled
206
205
  return if partial_data.nil? || partial_data == KEEP_ALIVE_RESPONSE
207
206
 
208
207
  events = @event_parser.parse(partial_data)
@@ -220,7 +219,7 @@ module SplitIoClient
220
219
  req << "SplitSDKMachineName: #{@config.machine_name}\r\n"
221
220
  req << "SplitSDKClientKey: #{@api_key.split(//).last(4).join}\r\n" unless @api_key.nil?
222
221
  req << "Cache-Control: no-cache\r\n\r\n"
223
- @config.logger.debug("Request info: #{req}")
222
+ @config.logger.debug("Request info: #{req}") if @config.debug_enabled
224
223
  req
225
224
  end
226
225
 
@@ -255,7 +254,7 @@ module SplitIoClient
255
254
  def push_status(status)
256
255
  return if status.nil?
257
256
 
258
- @config.logger.debug("Pushing new sse status: #{status}")
257
+ @config.logger.debug("Pushing new sse status: #{status}") if @config.debug_enabled
259
258
  @status_queue.push(status)
260
259
  end
261
260
  end
@@ -29,14 +29,14 @@ module SplitIoClient
29
29
 
30
30
  events
31
31
  rescue StandardError => e
32
- @config.logger.debug("Error during parsing a event: #{e.inspect}")
32
+ @config.logger.debug("Error during parsing a event: #{e.inspect}") if @config.debug_enabled
33
33
  []
34
34
  end
35
35
 
36
36
  def first_event(raw_data)
37
37
  raw_data.split("\n")[0].split(' ')[1].to_i
38
38
  rescue StandardError => e
39
- @config.logger.debug("Error parsing first event: #{e.inspect}")
39
+ @config.logger.error("Error parsing first event: #{e.inspect}")
40
40
  BAD_REQUEST_CODE
41
41
  end
42
42
 
@@ -42,12 +42,12 @@ module SplitIoClient
42
42
  @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::STREAMING_STATUS, DISABLED)
43
43
  push_status(Constants::PUSH_SUBSYSTEM_OFF)
44
44
  else
45
- @config.logger.error("Incorrect event type: #{incoming_notification}")
45
+ @config.logger.error("Incorrect event type: #{incoming_notification}") if @config.debug_enabled
46
46
  end
47
47
  end
48
48
 
49
49
  def process_event_occupancy(channel, publishers)
50
- @config.logger.debug("Processed occupancy event with #{publishers} publishers. Channel: #{channel}")
50
+ @config.logger.debug("Processed occupancy event with #{publishers} publishers. Channel: #{channel}") if @config.debug_enabled
51
51
 
52
52
  update_publishers(channel, publishers)
53
53
 
@@ -76,7 +76,7 @@ module SplitIoClient
76
76
  end
77
77
 
78
78
  def push_status(status)
79
- @config.logger.debug("Pushing occupancy status: #{status}")
79
+ @config.logger.debug("Pushing occupancy status: #{status}") if @config.debug_enabled
80
80
  @status_queue.push(status)
81
81
  end
82
82
  end
@@ -14,13 +14,13 @@ module SplitIoClient
14
14
 
15
15
  def add_to_queue(change_number, segment_name)
16
16
  item = { change_number: change_number, segment_name: segment_name }
17
- @config.logger.debug("SegmentsWorker add to queue #{item}")
17
+ @config.logger.debug("SegmentsWorker add to queue #{item}") if @config.debug_enabled
18
18
  @queue.push(item)
19
19
  end
20
20
 
21
21
  def start
22
22
  if @running.value
23
- @config.logger.debug('segments worker already running.')
23
+ @config.logger.debug('segments worker already running.') if @config.debug_enabled
24
24
  return
25
25
  end
26
26
 
@@ -30,7 +30,7 @@ module SplitIoClient
30
30
 
31
31
  def stop
32
32
  unless @running.value
33
- @config.logger.debug('segments worker not running.')
33
+ @config.logger.debug('segments worker not running.') if @config.debug_enabled
34
34
  return
35
35
  end
36
36
 
@@ -44,7 +44,7 @@ module SplitIoClient
44
44
  while (item = @queue.pop)
45
45
  segment_name = item[:segment_name]
46
46
  cn = item[:change_number]
47
- @config.logger.debug("SegmentsWorker change_number dequeue #{segment_name}, #{cn}")
47
+ @config.logger.debug("SegmentsWorker change_number dequeue #{segment_name}, #{cn}") if @config.debug_enabled
48
48
 
49
49
  @synchronizer.fetch_segment(segment_name, cn)
50
50
  end
@@ -18,7 +18,7 @@ module SplitIoClient
18
18
 
19
19
  def start
20
20
  if @running.value
21
- @config.logger.debug('feature_flags_worker already running.')
21
+ @config.logger.debug('feature_flags_worker already running.') if @config.debug_enabled
22
22
  return
23
23
  end
24
24
 
@@ -28,7 +28,7 @@ module SplitIoClient
28
28
 
29
29
  def stop
30
30
  unless @running.value
31
- @config.logger.debug('feature_flags_worker not running.')
31
+ @config.logger.debug('feature_flags_worker not running.') if @config.debug_enabled
32
32
  return
33
33
  end
34
34
 
@@ -37,7 +37,7 @@ module SplitIoClient
37
37
  end
38
38
 
39
39
  def add_to_queue(notification)
40
- @config.logger.debug("feature_flags_worker add to queue #{notification.data['changeNumber']}")
40
+ @config.logger.debug("feature_flags_worker add to queue #{notification.data['changeNumber']}") if @config.debug_enabled
41
41
  @queue.push(notification)
42
42
  end
43
43
 
@@ -52,7 +52,9 @@ module SplitIoClient
52
52
 
53
53
  def perform
54
54
  while (notification = @queue.pop)
55
- @config.logger.debug("feature_flags_worker change_number dequeue #{notification.data['changeNumber']}")
55
+ if @config.debug_enabled
56
+ @config.logger.debug("feature_flags_worker change_number dequeue #{notification.data['changeNumber']}")
57
+ end
56
58
  case notification.data['type']
57
59
  when SSE::EventSource::EventTypes::SPLIT_UPDATE
58
60
  success = update_feature_flag(notification)
@@ -117,7 +119,9 @@ module SplitIoClient
117
119
  def kill_feature_flag(notification)
118
120
  return if @feature_flags_repository.get_change_number.to_i > notification.data['changeNumber']
119
121
 
120
- @config.logger.debug("feature_flags_worker kill #{notification.data['splitName']}, #{notification.data['changeNumber']}")
122
+ if @config.debug_enabled
123
+ @config.logger.debug("feature_flags_worker kill #{notification.data['splitName']}, #{notification.data['changeNumber']}")
124
+ end
121
125
  @feature_flags_repository.kill(notification.data['changeNumber'],
122
126
  notification.data['splitName'],
123
127
  notification.data['defaultTreatment'])
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '8.10.1-rc.2'
2
+ VERSION = '8.11.0'
3
3
  end
@@ -66,6 +66,11 @@ require 'splitclient-rb/engine/api/telemetry_api'
66
66
  require 'splitclient-rb/engine/common/impressions_counter'
67
67
  require 'splitclient-rb/engine/common/impressions_manager'
68
68
  require 'splitclient-rb/engine/common/noop_impressions_counter'
69
+ require 'splitclient-rb/engine/events/events_manager_config.rb'
70
+ require 'splitclient-rb/engine/events/events_manager.rb'
71
+ require 'splitclient-rb/engine/events/events_task.rb'
72
+ require 'splitclient-rb/engine/events/events_delivery.rb'
73
+ require 'splitclient-rb/engine/events/noop_events_queue.rb'
69
74
  require 'splitclient-rb/engine/parser/condition'
70
75
  require 'splitclient-rb/engine/parser/partition'
71
76
  require 'splitclient-rb/engine/parser/evaluator'
@@ -112,6 +117,13 @@ require 'splitclient-rb/engine/models/split_http_response'
112
117
  require 'splitclient-rb/engine/models/evaluation_options'
113
118
  require 'splitclient-rb/engine/models/fallback_treatment.rb'
114
119
  require 'splitclient-rb/engine/models/fallback_treatments_configuration.rb'
120
+ require 'splitclient-rb/engine/models/events_metadata.rb'
121
+ require 'splitclient-rb/engine/models/sdk_event_type.rb'
122
+ require 'splitclient-rb/engine/models/sdk_event.rb'
123
+ require 'splitclient-rb/engine/models/sdk_internal_event.rb'
124
+ require 'splitclient-rb/engine/models/sdk_internal_event_notification.rb'
125
+ require 'splitclient-rb/engine/models/valid_sdk_event.rb'
126
+ require 'splitclient-rb/engine/models/event_active_subscriptions.rb'
115
127
  require 'splitclient-rb/engine/auth_api_client'
116
128
  require 'splitclient-rb/engine/back_off'
117
129
  require 'splitclient-rb/engine/fallback_treatment_calculator.rb'
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.10.1.pre.rc.2
4
+ version: 8.11.0
5
5
  platform: java
6
6
  authors:
7
7
  - Split Software
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-01-22 00:00:00.000000000 Z
10
+ date: 2026-03-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: allocation_stats
@@ -393,6 +393,7 @@ files:
393
393
  - CONTRIBUTORS-GUIDE.md
394
394
  - Gemfile
395
395
  - LICENSE
396
+ - NOTICE.txt
396
397
  - README.md
397
398
  - Rakefile
398
399
  - ext/murmurhash/MurmurHash3.java
@@ -454,6 +455,11 @@ files:
454
455
  - lib/splitclient-rb/engine/common/impressions_manager.rb
455
456
  - lib/splitclient-rb/engine/common/noop_impressions_counter.rb
456
457
  - lib/splitclient-rb/engine/evaluator/splitter.rb
458
+ - lib/splitclient-rb/engine/events/events_delivery.rb
459
+ - lib/splitclient-rb/engine/events/events_manager.rb
460
+ - lib/splitclient-rb/engine/events/events_manager_config.rb
461
+ - lib/splitclient-rb/engine/events/events_task.rb
462
+ - lib/splitclient-rb/engine/events/noop_events_queue.rb
457
463
  - lib/splitclient-rb/engine/fallback_treatment_calculator.rb
458
464
  - lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb
459
465
  - lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb
@@ -489,13 +495,20 @@ files:
489
495
  - lib/splitclient-rb/engine/matchers/whitelist_matcher.rb
490
496
  - lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb
491
497
  - lib/splitclient-rb/engine/models/evaluation_options.rb
498
+ - lib/splitclient-rb/engine/models/event_active_subscriptions.rb
499
+ - lib/splitclient-rb/engine/models/events_metadata.rb
492
500
  - lib/splitclient-rb/engine/models/fallback_treatment.rb
493
501
  - lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb
494
502
  - lib/splitclient-rb/engine/models/label.rb
503
+ - lib/splitclient-rb/engine/models/sdk_event.rb
504
+ - lib/splitclient-rb/engine/models/sdk_event_type.rb
505
+ - lib/splitclient-rb/engine/models/sdk_internal_event.rb
506
+ - lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb
495
507
  - lib/splitclient-rb/engine/models/segment_type.rb
496
508
  - lib/splitclient-rb/engine/models/split.rb
497
509
  - lib/splitclient-rb/engine/models/split_http_response.rb
498
510
  - lib/splitclient-rb/engine/models/treatment.rb
511
+ - lib/splitclient-rb/engine/models/valid_sdk_event.rb
499
512
  - lib/splitclient-rb/engine/parser/condition.rb
500
513
  - lib/splitclient-rb/engine/parser/evaluator.rb
501
514
  - lib/splitclient-rb/engine/parser/partition.rb