Skip to content

Release 8.6.0 #563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 67 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
0de56c7
upgrade libs
Jan 24, 2025
f75ab32
revert spec change
Jan 24, 2025
b2fe161
polish
Jan 24, 2025
fdba26d
polish
Jan 24, 2025
273f7cd
removed rexml and upgraded webmock
Jan 24, 2025
5217f63
fix test
Jan 24, 2025
79d1565
fix test
Jan 24, 2025
20368af
Merge pull request #546 from splitio/fix-vulnerabilities
chillaq Feb 4, 2025
f856352
Added rbs cache class
chillaq Apr 22, 2025
4bc3716
Added RBS matcher
chillaq Apr 29, 2025
80c207f
updated matcher and evaluator
chillaq May 6, 2025
ea9928a
Added RBS support for fetcher
chillaq May 8, 2025
8a9defb
polish
chillaq May 8, 2025
207532c
Updated SSE and synchronizer classes
chillaq May 9, 2025
62eae92
polish
chillaq May 9, 2025
a7b618a
Merge pull request #547 from splitio/rbs-cache
chillaq May 9, 2025
0480e78
polish
chillaq May 9, 2025
98ba675
polish
chillaq May 9, 2025
80cd16a
polish
chillaq May 10, 2025
09829cf
Merge pull request #548 from splitio/rbs-matcher
chillaq May 12, 2025
b72babe
Merge pull request #549 from splitio/rbs-fetcher
chillaq May 12, 2025
14cee73
Integrations tests
chillaq May 14, 2025
e370d95
Merge pull request #550 from splitio/rbs-sse
chillaq May 14, 2025
5ac3978
Merge branch 'feature/rule-based-segments' into rbs-integrations-tests
chillaq May 14, 2025
61e502f
Added old spec support
chillaq May 15, 2025
2e54131
update tests
chillaq May 15, 2025
b7013d4
polish
chillaq May 15, 2025
1952d9d
polish
chillaq May 16, 2025
a2ce544
fix sse issue
chillaq May 16, 2025
acb3233
Merge branch 'rbs-old-spec' of https://github.com/splitio/ruby-client…
chillaq May 16, 2025
1566bfb
polish
chillaq May 16, 2025
ecd007b
updated split api
chillaq May 16, 2025
f5da2a7
Merge pull request #553 from splitio/rbs-oldspec-restore-since
chillaq May 16, 2025
3a33ffc
updated util and test
chillaq May 19, 2025
d880ce2
Merge branch 'rbs-old-spec' of https://github.com/splitio/ruby-client…
chillaq May 19, 2025
4a8e7d8
Merge branch 'rbs-integrations-tests' into rbs-old-spec
chillaq May 19, 2025
3fe83c5
fix test
chillaq May 20, 2025
dcc45ce
Merge branch 'rbs-old-spec' of https://github.com/splitio/ruby-client…
chillaq May 20, 2025
578cb0c
Merge pull request #552 from splitio/rbs-old-spec
chillaq May 20, 2025
d266ac9
polish
chillaq May 20, 2025
1ba8c0b
Merge pull request #551 from splitio/rbs-integrations-tests
chillaq May 20, 2025
a35a61c
Fix proxy error
chillaq May 21, 2025
f9ddf9a
Merge pull request #554 from splitio/rbs-fix-proxy
chillaq May 21, 2025
bef29ad
polish
chillaq May 22, 2025
16125cf
Added matcher
chillaq Jun 4, 2025
e58a513
Updated evaluator
chillaq Jun 4, 2025
aa33699
Added integration tests
chillaq Jun 5, 2025
f4ced37
polish
chillaq Jun 5, 2025
0a8d1bd
polish
chillaq Jun 5, 2025
29d456b
Merge pull request #555 from splitio/T-FME-4180-prereq-matcher
chillaq Jun 5, 2025
71c04c2
Merge pull request #556 from splitio/T-FME-4181-prereq-evaluator
chillaq Jun 5, 2025
117a962
Revert "Updated evaluator"
chillaq Jun 5, 2025
70c5759
Merge pull request #558 from splitio/revert-556-T-FME-4181-prereq-eva…
chillaq Jun 5, 2025
3c987da
polish
chillaq Jun 5, 2025
b430ac4
Merge branch 'feature/prerequisites' into T-FME-4160-prereq-integrations
chillaq Jun 5, 2025
a10f235
Merge pull request #557 from splitio/T-FME-4160-prereq-integrations
chillaq Jun 10, 2025
b584f05
Merge branch 'development' into feature/rule-based-segments
chillaq Jun 10, 2025
2a5da61
polish
chillaq Jun 10, 2025
f8a3c46
polish
chillaq Jun 10, 2025
fd1e59e
Merge pull request #560 from splitio/feature/prerequisites
chillaq Jun 11, 2025
e728d3e
Added missing label
chillaq Jun 11, 2025
fc69af4
updated changes and version
chillaq Jun 11, 2025
4d4c830
polish
chillaq Jun 11, 2025
a82cf74
Merge pull request #559 from splitio/feature/rule-based-segments
chillaq Jun 11, 2025
31a145d
Update CHANGES.txt
chillaq Jun 16, 2025
3fd624e
Update CHANGES.txt
chillaq Jun 17, 2025
6969b26
Merge pull request #562 from splitio/release-8.6.0
chillaq Jun 17, 2025
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
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
CHANGES

8.6.0 (Jun 17, 2025)
- Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK.
- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.

8.5.0 (Jan 17, 2025)
- Fixed high cpu usage when unique keys are cleared every 24 hours.
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs.
Expand Down
6 changes: 6 additions & 0 deletions lib/splitclient-rb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require 'splitclient-rb/cache/repositories/splits_repository'
require 'splitclient-rb/cache/repositories/events_repository'
require 'splitclient-rb/cache/repositories/impressions_repository'
require 'splitclient-rb/cache/repositories/rule_based_segments_repository'
require 'splitclient-rb/cache/repositories/events/memory_repository'
require 'splitclient-rb/cache/repositories/events/redis_repository'
require 'splitclient-rb/cache/repositories/flag_sets/memory_repository'
Expand All @@ -47,6 +48,7 @@
require 'splitclient-rb/helpers/decryption_helper'
require 'splitclient-rb/helpers/util'
require 'splitclient-rb/helpers/repository_helper'
require 'splitclient-rb/helpers/evaluator_helper'
require 'splitclient-rb/split_factory'
require 'splitclient-rb/split_factory_builder'
require 'splitclient-rb/split_config'
Expand Down Expand Up @@ -96,13 +98,17 @@
require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher'
require 'splitclient-rb/engine/matchers/between_semver_matcher'
require 'splitclient-rb/engine/matchers/in_list_semver_matcher'
require 'splitclient-rb/engine/matchers/rule_based_segment_matcher'
require 'splitclient-rb/engine/matchers/prerequisites_matcher'
require 'splitclient-rb/engine/evaluator/splitter'
require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker'
require 'splitclient-rb/engine/impressions/unique_keys_tracker'
require 'splitclient-rb/engine/metrics/binary_search_latency_tracker'
require 'splitclient-rb/engine/models/split'
require 'splitclient-rb/engine/models/label'
require 'splitclient-rb/engine/models/segment_type'
require 'splitclient-rb/engine/models/treatment'
require 'splitclient-rb/engine/models/split_http_response'
require 'splitclient-rb/engine/auth_api_client'
require 'splitclient-rb/engine/back_off'
require 'splitclient-rb/engine/push_manager'
Expand Down
16 changes: 9 additions & 7 deletions lib/splitclient-rb/cache/fetchers/split_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ module SplitIoClient
module Cache
module Fetchers
class SplitFetcher
attr_reader :splits_repository
attr_reader :splits_repository, :rule_based_segments_repository

def initialize(splits_repository, api_key, config, telemetry_runtime_producer)
def initialize(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer)
@splits_repository = splits_repository
@rule_based_segments_repository = rule_based_segments_repository
@api_key = api_key
@config = config
@semaphore = Mutex.new
Expand All @@ -23,10 +24,11 @@ def call

def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
@semaphore.synchronize do
data = splits_since(@splits_repository.get_change_number, fetch_options)

SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:splits], data[:till], @config)
data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options)
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage)
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config)
@splits_repository.set_segment_names(data[:segment_names])
@rule_based_segments_repository.set_segment_names(data[:segment_names])
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled

{ segment_names: data[:segment_names], success: true }
Expand Down Expand Up @@ -55,8 +57,8 @@ def splits_thread
end
end

def splits_since(since, fetch_options = { cache_control_headers: false, till: nil })
splits_api.since(since, fetch_options)
def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil })
splits_api.since(since, since_rbs, fetch_options)
end

def splits_api
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
require 'concurrent'

module SplitIoClient
module Cache
module Repositories
class RuleBasedSegmentsRepository < Repository
attr_reader :adapter
DEFAULT_CONDITIONS_TEMPLATE = [{
conditionType: "ROLLOUT",
matcherGroup: {
combiner: "AND",
matchers: [
{
keySelector: nil,
matcherType: "ALL_KEYS",
negate: false,
userDefinedSegmentMatcherData: nil,
whitelistMatcherData: nil,
unaryNumericMatcherData: nil,
betweenMatcherData: nil,
dependencyMatcherData: nil,
booleanMatcherData: nil,
stringMatcherData: nil
}]
}
}]
TILL_PREFIX = '.rbsegments.till'
RB_SEGMENTS_PREFIX = '.rbsegment.'
REGISTERED_PREFIX = '.segments.registered'

def initialize(config)
super(config)
@adapter = case @config.cache_adapter.class.to_s
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
SplitIoClient::Cache::Adapters::CacheAdapter.new(@config)
else
@config.cache_adapter
end
unless @config.mode.equal?(:consumer)
@adapter.set_string(namespace_key(TILL_PREFIX), '-1')
@adapter.initialize_map(namespace_key(REGISTERED_PREFIX))
end
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)
end

def get_rule_based_segment(name)
rule_based_segment = @adapter.string(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}"))

JSON.parse(rule_based_segment, symbolize_names: true) if rule_based_segment
end

def rule_based_segment_names
@adapter.find_strings_by_prefix(namespace_key(RB_SEGMENTS_PREFIX))
.map { |rule_based_segment_names| rule_based_segment_names.gsub(namespace_key(RB_SEGMENTS_PREFIX), '') }
end

def set_change_number(since)
@adapter.set_string(namespace_key(TILL_PREFIX), since)
end

def get_change_number
@adapter.string(namespace_key(TILL_PREFIX))
end

def set_segment_names(names)
return if names.nil? || names.empty?

names.each do |name|
@adapter.add_to_set(namespace_key(REGISTERED_PREFIX), name)
end
end

def exists?(name)
@adapter.exists?(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}"))
end

def clear
@adapter.clear(namespace_key)
end

def contains?(segment_names)
return false if rule_based_segment_names.empty?
return set(segment_names).subset?(rule_based_segment_names)
end

private

def add_rule_based_segment(rule_based_segment)
return unless rule_based_segment[:name]

if check_undefined_matcher(rule_based_segment)
@config.logger.warn("Rule based segment #{rule_based_segment[:name]} has undefined matcher, setting conditions to default template.")
rule_based_segment[:conditions] = RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE
end

@adapter.set_string(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}"), rule_based_segment.to_json)
end

def check_undefined_matcher(rule_based_segment)
for condition in rule_based_segment[:conditions]
for matcher in condition[:matcherGroup][:matchers]
if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}")
@config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{rule_based_segment[:name]}")
return true
end
end
end
return false
end

def remove_rule_based_segment(rule_based_segment)
@adapter.delete(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}"))
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ def segment_keys_count
0
end

def contains?(segment_names)
if segment_names.empty?
return false
end
return segment_names.to_set.subset?(used_segment_names.to_set)
end

private

def segment_data(name)
Expand Down
30 changes: 19 additions & 11 deletions lib/splitclient-rb/cache/repositories/splits_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class SplitsRepository < Repository
],
label: "targeting rule type unsupported by sdk"
}]
TILL_PREFIX = '.splits.till'
SPLIT_PREFIX = '.split.'
READY_PREFIX = '.splits.ready'

def initialize(config, flag_sets_repository, flag_set_filter)
super(config)
Expand All @@ -43,10 +46,7 @@ def initialize(config, flag_sets_repository, flag_set_filter)
end
@flag_sets = flag_sets_repository
@flag_set_filter = flag_set_filter
unless @config.mode.equal?(:consumer)
@adapter.set_string(namespace_key('.splits.till'), '-1')
@adapter.initialize_map(namespace_key('.segments.registered'))
end
initialize_keys
end

def update(to_add, to_delete, new_change_number)
Expand Down Expand Up @@ -87,16 +87,16 @@ def traffic_type_exists(tt_name)

# Return an array of Split Names excluding control keys like splits.till
def split_names
@adapter.find_strings_by_prefix(namespace_key('.split.'))
.map { |split| split.gsub(namespace_key('.split.'), '') }
@adapter.find_strings_by_prefix(namespace_key(SPLIT_PREFIX))
.map { |split| split.gsub(namespace_key(SPLIT_PREFIX), '') }
end

def set_change_number(since)
@adapter.set_string(namespace_key('.splits.till'), since)
@adapter.set_string(namespace_key(TILL_PREFIX), since)
end

def get_change_number
@adapter.string(namespace_key('.splits.till'))
@adapter.string(namespace_key(TILL_PREFIX))
end

def set_segment_names(names)
Expand All @@ -112,21 +112,22 @@ def exists?(name)
end

def ready?
@adapter.string(namespace_key('.splits.ready')).to_i != -1
@adapter.string(namespace_key(READY_PREFIX)).to_i != -1
end

def not_ready!
@adapter.set_string(namespace_key('.splits.ready'), -1)
@adapter.set_string(namespace_key(READY_PREFIX), -1)
end

def ready!
@adapter.set_string(namespace_key('.splits.ready'), Time.now.utc.to_i)
@adapter.set_string(namespace_key(READY_PREFIX), Time.now.utc.to_i)
end

def clear
@tt_cache.clear

@adapter.clear(namespace_key)
initialize_keys
end

def kill(change_number, split_name, default_treatment)
Expand Down Expand Up @@ -167,6 +168,13 @@ def flag_set_filter

private

def initialize_keys
unless @config.mode.equal?(:consumer)
@adapter.set_string(namespace_key(TILL_PREFIX), '-1')
@adapter.initialize_map(namespace_key('.segments.registered'))
end
end

def add_feature_flag(split)
return unless split[:name]
existing_split = get_split(split[:name])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def build_split(feature, treatments)
seed: 2_089_907_429,
defaultTreatment: 'control_treatment',
configurations: build_configurations(treatments),
conditions: build_conditions(treatments)
conditions: build_conditions(treatments),
prerequisites: []
}
end

Expand Down
2 changes: 2 additions & 0 deletions lib/splitclient-rb/clients/split_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage
@api_key = sdk_key
@splits_repository = repositories[:splits]
@segments_repository = repositories[:segments]
@rule_based_segments_repository = repositories[:rule_based_segments]
@impressions_repository = repositories[:impressions]
@events_repository = repositories[:events]
@status_manager = status_manager
Expand Down Expand Up @@ -115,6 +116,7 @@ def destroy

@splits_repository.clear
@segments_repository.clear
@rule_based_segments_repository.clear

SplitIoClient.load_factory_registry
SplitIoClient.split_factory_registry.remove(@api_key)
Expand Down
8 changes: 8 additions & 0 deletions lib/splitclient-rb/engine/api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ def get_api(url, api_key, params = {}, cache_control_headers = false)

@config.split_logger.log_if_debug("GET #{url} proxy: #{api_client.proxy}")
end

rescue Zlib::GzipFile::Error => e
@config.logger.warn("Incorrect formatted response exception: #{e}\n")
SplitIoClient::Engine::Models::SplitHttpResponse.new(400, '', true)

rescue StandardError => e
@config.logger.warn("#{e}\nURL:#{url}\nparams:#{params}")
raise e, 'Split SDK failed to connect to backend to retrieve information', e.backtrace
Expand Down Expand Up @@ -50,6 +55,9 @@ def post_api(url, api_key, data, headers = {}, params = {})
raise e, 'Split SDK failed to connect to backend to post information', e.backtrace
end

def sdk_url_overriden?
@config.sdk_url_overriden?
end
private

def api_client
Expand Down
Loading