Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1c58b6c
added models classes
chillaq Feb 9, 2026
725c309
Merge pull request #610 from splitio/FME-12883-events-models
chillaq Feb 13, 2026
178bcf4
added events task and polishing.
chillaq Feb 13, 2026
6de8c7c
fixed loop
chillaq Feb 13, 2026
d620110
updated split, segment and rbsegment repositories
chillaq Feb 15, 2026
31bd303
Merge pull request #611 from splitio/FME-12889-events-task
chillaq Mar 2, 2026
e34a68c
Merge pull request #613 from splitio/FME-12890-events-repositories
chillaq Mar 2, 2026
919bb8e
added firing sdk ready event
chillaq Mar 2, 2026
55e477a
added events manager class
chillaq Mar 4, 2026
394b9fc
added events delivery and tests
chillaq Mar 4, 2026
37fd6f3
polish
chillaq Mar 4, 2026
79ab3d0
added test
chillaq Mar 5, 2026
58cb0b9
updated factory and split_client with integration tests
chillaq Mar 6, 2026
bf98f26
updated tests
chillaq Mar 6, 2026
553dae2
Merge pull request #614 from splitio/FME-13561-ready-event
chillaq Mar 7, 2026
bea83f5
Merge pull request #615 from splitio/FME-13560-events-manager
chillaq Mar 7, 2026
259e042
Merge pull request #616 from splitio/FME-13562-factory-integration
chillaq Mar 7, 2026
4dd9686
- fixed multiple events fire with segment update
chillaq Mar 11, 2026
455e84d
polishing
chillaq Mar 11, 2026
55fa95e
used relative load for events models classes
chillaq Mar 11, 2026
b289f74
fixed sync_manager errors
chillaq Mar 11, 2026
8b2ccbb
polish
chillaq Mar 11, 2026
df3f78e
polish
chillaq Mar 11, 2026
40fc8f0
rolled back include in events manager
chillaq Mar 11, 2026
5fd8e94
updated license, changes and version
chillaq Mar 12, 2026
96d2fbd
Merge pull request #617 from splitio/FME-13964-fix-firing-events-twice
chillaq Mar 12, 2026
1d6b7e6
Merge pull request #618 from splitio/feature/sdk-events
chillaq Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
CHANGES

8.11.0 (Mar, 12, 2026)
- Added the ability to listen to different events triggered by the SDK. Read more in our docs.
- SDK_UPDATE notify when a flag or user segment has changed
- SDK_READY notify when the SDK is ready to evaluate

8.10.1 (Jan 28, 2025)
- Fixed rule-based segment matcher to exit when a conition is met.
- Fixed impressions properties format in redis mode.
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Apache License
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2025 Harness Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand Down
12 changes: 12 additions & 0 deletions lib/splitclient-rb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
require 'splitclient-rb/engine/common/impressions_counter'
require 'splitclient-rb/engine/common/impressions_manager'
require 'splitclient-rb/engine/common/noop_impressions_counter'
require 'splitclient-rb/engine/events/events_manager_config.rb'
require 'splitclient-rb/engine/events/events_manager.rb'
require 'splitclient-rb/engine/events/events_task.rb'
require 'splitclient-rb/engine/events/events_delivery.rb'
require 'splitclient-rb/engine/events/noop_events_queue.rb'
require 'splitclient-rb/engine/parser/condition'
require 'splitclient-rb/engine/parser/partition'
require 'splitclient-rb/engine/parser/evaluator'
Expand Down Expand Up @@ -112,6 +117,13 @@
require 'splitclient-rb/engine/models/evaluation_options'
require 'splitclient-rb/engine/models/fallback_treatment.rb'
require 'splitclient-rb/engine/models/fallback_treatments_configuration.rb'
require 'splitclient-rb/engine/models/events_metadata.rb'
require 'splitclient-rb/engine/models/sdk_event_type.rb'
require 'splitclient-rb/engine/models/sdk_event.rb'
require 'splitclient-rb/engine/models/sdk_internal_event.rb'
require 'splitclient-rb/engine/models/sdk_internal_event_notification.rb'
require 'splitclient-rb/engine/models/valid_sdk_event.rb'
require 'splitclient-rb/engine/models/event_active_subscriptions.rb'
require 'splitclient-rb/engine/auth_api_client'
require 'splitclient-rb/engine/back_off'
require 'splitclient-rb/engine/fallback_treatment_calculator.rb'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class RuleBasedSegmentsRepository < Repository
RB_SEGMENTS_PREFIX = '.rbsegment.'
REGISTERED_PREFIX = '.segments.registered'

def initialize(config)
def initialize(config, internal_events_queue)
super(config)
@adapter = case @config.cache_adapter.class.to_s
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
Expand All @@ -40,12 +40,25 @@ def initialize(config)
@adapter.set_string(namespace_key(TILL_PREFIX), '-1')
@adapter.initialize_map(namespace_key(REGISTERED_PREFIX))
end
@internal_events_queue = internal_events_queue
end

def update(to_add, to_delete, new_change_number)
to_add.each{ |rule_based_segment| add_rule_based_segment(rule_based_segment) }
to_delete.each{ |rule_based_segment| remove_rule_based_segment(rule_based_segment) }
set_change_number(new_change_number)

if to_add.length > 0 || to_delete.length > 0
@internal_events_queue.push(
SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
SplitIoClient::Engine::Models::SdkInternalEvent::RB_SEGMENTS_UPDATED,
SplitIoClient::Engine::Models::EventsMetadata.new(
SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
[]
)
)
)
end
end

def get_rule_based_segment(name)
Expand Down
15 changes: 13 additions & 2 deletions lib/splitclient-rb/cache/repositories/segments_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class SegmentsRepository < Repository

attr_reader :adapter

def initialize(config)
def initialize(config, internal_events_queue)
super(config)
@adapter = case @config.cache_adapter.class.to_s
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
Expand All @@ -15,16 +15,27 @@ def initialize(config)
@config.cache_adapter
end
@adapter.set_bool(namespace_key('.ready'), false) unless @config.mode.equal?(:consumer)
@internal_events_queue = internal_events_queue
end

# Receives segment data, adds and removes segements from the store
def add_to_segment(segment)
name = segment[:name]

@adapter.initialize_set(segment_data(name)) unless @adapter.exists?(segment_data(name))

add_keys(name, segment[:added])
remove_keys(name, segment[:removed])
if segment[:added].length > 0 || segment[:removed].length > 0
@internal_events_queue.push(
SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
SplitIoClient::Engine::Models::SdkInternalEvent::SEGMENTS_UPDATED,
SplitIoClient::Engine::Models::EventsMetadata.new(
SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
[]
)
)
)
end
end

def get_segment_keys(name)
Expand Down
24 changes: 23 additions & 1 deletion lib/splitclient-rb/cache/repositories/splits_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class SplitsRepository < Repository
SPLIT_PREFIX = '.split.'
READY_PREFIX = '.splits.ready'

def initialize(config, flag_sets_repository, flag_set_filter)
def initialize(config, flag_sets_repository, flag_set_filter, internal_events_queue)
super(config)
@tt_cache = {}
@adapter = case @config.cache_adapter.class.to_s
Expand All @@ -46,13 +46,26 @@ def initialize(config, flag_sets_repository, flag_set_filter)
end
@flag_sets = flag_sets_repository
@flag_set_filter = flag_set_filter
@internal_events_queue = internal_events_queue
initialize_keys
end

def update(to_add, to_delete, new_change_number)
to_add.each{ |feature_flag| add_feature_flag(feature_flag) }
to_delete.each{ |feature_flag| remove_feature_flag(feature_flag) }
set_change_number(new_change_number)

if to_add.length > 0 || to_delete.length > 0
@internal_events_queue.push(
SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
SplitIoClient::Engine::Models::SdkInternalEvent::FLAGS_UPDATED,
SplitIoClient::Engine::Models::EventsMetadata.new(
SplitIoClient::Engine::Models::SdkEventType::FLAG_UPDATE,
to_add.map {|flag| flag[:name]} | to_delete.map {|flag| flag[:name]}
)
)
)
end
end

def get_split(name)
Expand Down Expand Up @@ -140,6 +153,15 @@ def kill(change_number, split_name, default_treatment)
split[:changeNumber] = change_number

@adapter.set_string(namespace_key(".split.#{split_name}"), split.to_json)
@internal_events_queue.push(
SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
SplitIoClient::Engine::Models::SdkInternalEvent::FLAG_KILLED_NOTIFICATION,
SplitIoClient::Engine::Models::EventsMetadata.new(
SplitIoClient::Engine::Models::SdkEventType::FLAG_UPDATE,
[split_name]
)
)
)
end

def splits_count
Expand Down
15 changes: 12 additions & 3 deletions lib/splitclient-rb/clients/split_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SplitClient
# @param sdk_key [String] the SDK key for your split account
#
# @return [SplitIoClient] split.io client instance
def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator)
def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator, events_manager)
@api_key = sdk_key
@splits_repository = repositories[:splits]
@segments_repository = repositories[:segments]
Expand All @@ -33,6 +33,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage
@split_validator = split_validator
@evaluator = evaluator
@fallback_treatment_calculator = fallback_treatment_calculator
@events_manager = events_manager
end

def get_treatment(
Expand Down Expand Up @@ -117,11 +118,11 @@ def destroy
@config.logger.info('Split client shutdown started...') if @config.debug_enabled
if !@config.cache_adapter.is_a?(SplitIoClient::Cache::Adapters::RedisAdapter) && @config.impressions_mode != :none &&
(!@impressions_repository.empty? || !@events_repository.empty?)
@config.logger.debug("Impressions and/or Events cache is not empty")
@config.logger.debug("Impressions and/or Events cache is not empty") if @config.debug_enabled
# Adding small delay to ensure sender threads are fully running
sleep(0.1)
if !@config.threads.key?(:impressions_sender) || !@config.threads.key?(:events_sender)
@config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.")
@config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.") if @config.debug_enabled
@config.threads[:start_sdk].join(5) if @config.threads.key?(:start_sdk)
end
end
Expand Down Expand Up @@ -176,6 +177,14 @@ def block_until_ready(time = nil)
@status_manager.wait_until_ready(time) if @status_manager
end

def register(sdk_event, handler)
@events_manager.register(sdk_event, handler)
end

def unregister(sdk_event, handler)
@events_manager.unregister(sdk_event)
end

private

def check_properties_size(properties_size, msg = "Event not queued")
Expand Down
1 change: 1 addition & 0 deletions lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'faraday'
require 'stringio'

module SplitIoClient
module FaradayMiddleware
Expand Down
4 changes: 2 additions & 2 deletions lib/splitclient-rb/engine/api/splits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till

if check_last_proxy_check_timestamp
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
@config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.")
@config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.") if @config.debug_enabled
@old_spec_since = since
since = -1
since_rbs = -1
Expand All @@ -41,7 +41,7 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till

params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
@config.logger.debug("Fetching from splitChanges with #{params}: ")
@config.logger.debug("Fetching from splitChanges with #{params}: ") if @config.debug_enabled
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
if response.status == 414
@config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
Expand Down
10 changes: 7 additions & 3 deletions lib/splitclient-rb/engine/auth_api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ def authenticate(api_key)
return process_error(response) if response.status >= 400 && response.status < 500

@telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::TOKEN_SYNC, response.status.to_i)
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
if @config.debug_enabled
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
end
{ push_enabled: false, retry: true }
rescue StandardError => e
@config.logger.debug("AuthApiClient error: #{e.inspect}.")
@config.logger.debug("AuthApiClient error: #{e.inspect}.") if @config.debug_enabled
{ push_enabled: false, retry: false }
end

Expand All @@ -51,7 +53,9 @@ def decode_token(token)
end

def process_error(response)
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
if @config.debug_enabled
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}")
end
@telemetry_runtime_producer.record_auth_rejections if response.status == 401

{ push_enabled: false, retry: false }
Expand Down
20 changes: 20 additions & 0 deletions lib/splitclient-rb/engine/events/events_delivery.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module SplitIoClient
module Engine
module Events
class EventsDelivery
def initialize(config)
@config = config
end

def deliver(sdk_event, event_metadata, event_handler)
event_handler.call(event_metadata)
rescue StandardError => e
@config.logger.error("Exception when calling handler for Sdk Event #{sdk_event}")
@config.log_found_exception(__method__.to_s, e)
end
end
end
end
end
Loading
Loading