Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion lib/splitclient-rb/cache/fetchers/split_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def call
def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
@semaphore.synchronize do
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)
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])
Expand Down
13 changes: 9 additions & 4 deletions lib/splitclient-rb/cache/repositories/splits_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,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 @@ -127,6 +124,7 @@ 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 +165,13 @@ def flag_set_filter

private

def initialize_keys
unless @config.mode.equal?(:consumer)
@adapter.set_string(namespace_key('.splits.till'), '-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
3 changes: 3 additions & 0 deletions lib/splitclient-rb/engine/api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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
82 changes: 69 additions & 13 deletions lib/splitclient-rb/engine/api/splits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,72 @@ module Api
# Retrieves split definitions from the Split Backend
class Splits < Client

PROXY_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
SPEC_1_1 = "1.1"

def initialize(api_key, config, telemetry_runtime_producer)
super(config)
@api_key = api_key
@telemetry_runtime_producer = telemetry_runtime_producer
@flag_sets_filter = @config.flag_sets_filter
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
@last_proxy_check_timestamp = 0
@clear_storage = false
@old_spec_since = nil
end

def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
start = Time.now

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.")
@old_spec_since = since
since = -1
since_rbs = -1
fetch_options = { cache_control_headers: false, till: nil, sets: nil}
end

if @spec_version == Splits::SPEC_1_1
since = @old_spec_since unless @old_spec_since.nil?
params = { s: @spec_version, since: since }
@old_spec_since = nil
else
params = { s: @spec_version, since: since, rbSince: since_rbs }
end

params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs }
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}: ")
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.")
raise ApiException.new response.body, 414
end

if response.status == 400 and sdk_url_overriden? and @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
@config.logger.warn("Detected proxy response error, changing spec version from #{@spec_version} to #{Splits::SPEC_1_1} and re-fetching.")
@spec_version = Splits::SPEC_1_1
@last_proxy_check_timestamp = Time.now
return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till],
sets: fetch_options[:sets]})
end

if response.success?
result = objects_with_segment_names(response.body)
result = JSON.parse(response.body, symbolize_names: true)
if @spec_version == Splits::SPEC_1_1
result = convert_to_newSPEC(result)
end

result[:rbs][:d] = check_rbs_data(result[:rbs][:d])
result = objects_with_segment_names(result)

if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
@clear_storage = @last_proxy_check_timestamp != 0
@last_proxy_check_timestamp = 0
end

unless result[:ff][:d].empty?
@config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}")
end
Expand All @@ -52,30 +96,42 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till
end
end

def clear_storage
@clear_storage
end

private

def objects_with_segment_names(objects_json)
parsed_objects = JSON.parse(objects_json, symbolize_names: true)
def check_rbs_data(rbs_data)
rbs_data.each do |rb_segment|
rb_segment[:excluded] = {:keys => [], :segments => []} if rb_segment[:excluded].nil?
rb_segment[:excluded][:keys] = [] if rb_segment[:excluded][:keys].nil?
rb_segment[:excluded][:segments] = [] if rb_segment[:excluded][:segments].nil?
end
rbs_data
end

def objects_with_segment_names(parsed_objects)
parsed_objects[:segment_names] = Set.new
parsed_objects[:segment_names] =
parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits|
splits << Helpers::Util.segment_names_by_object(split, "IN_SEGMENT")
end.flatten

parsed_objects[:rbs][:d].each do |rule_based_segment|
parsed_objects[:segment_names].merge Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT")
end

parsed_objects[:rbs][:d].each do |rule_based_segment|
rule_based_segment[:excluded][:segments].each do |segment|
if segment[:type] == "standard"
parsed_objects[:segment_names].add(segment[:name])
end
end
parsed_objects[:segment_names].merge Helpers::Util.segment_names_in_rb_segment(rule_based_segment, "IN_SEGMENT")
end

parsed_objects
end

def check_last_proxy_check_timestamp
@spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS)
end

def convert_to_newSPEC(body)
{:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}}
end
end
end
end
46 changes: 27 additions & 19 deletions lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,10 @@ def match?(args)
rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name)
return false if rule_based_segment.nil?

if args[:value].nil? or args[:value].empty?
key = args[:matching_key]
else
key = args[:value]
end
key = update_key(args)
return false if rule_based_segment[:excluded][:keys].include?(key)

rule_based_segment[:excluded][:segments].each do |segment|
return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], key)

if segment[:type] == 'rule-based'
return false if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args)
end
end
return false unless check_excluded_segments(rule_based_segment, key, args)

matches = false
rule_based_segment[:conditions].each do |c|
Expand All @@ -52,13 +42,31 @@ def match?(args)

private

def match_rbs(rule_based_segment, args)
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, rule_based_segment[:name], @config)
return rbs_matcher.match?(
matching_key: args[:matching_key],
bucketing_key: args[:value],
attributes: args[:attributes]
)
def check_excluded_segments(rule_based_segment, key, args)
rule_based_segment[:excluded][:segments].each do |segment|
return false if segment[:type] == 'standard' && @segments_repository.in_segment?(segment[:name], key)

return false if segment[:type] == 'rule-based' && match_rbs(
@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args
)
end
true
end

def update_key(args)
if args[:value].nil? || args[:value].empty?
args[:matching_key]
else
args[:value]
end
end

def match_rbs(rule_based_segment, args)
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository,
rule_based_segment[:name], @config)
rbs_matcher.match?(matching_key: args[:matching_key],
bucketing_key: args[:value],
attributes: args[:attributes])
end
end
end
4 changes: 3 additions & 1 deletion lib/splitclient-rb/helpers/evaluator_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ def self.matcher_type(condition, segments_repository, rb_segment_repository)
segments_repository.adapter.pipelined do
condition.matchers.each do |matcher|
matchers << if matcher[:negate]
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository))
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition,
matcher, segments_repository,
rb_segment_repository))
else
matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)
end
Expand Down
21 changes: 14 additions & 7 deletions lib/splitclient-rb/helpers/repository_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module SplitIoClient
module Helpers
class RepositoryHelper
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config)
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage)
to_add = []
to_delete = []
feature_flags.each do |feature_flag|
Expand All @@ -13,19 +13,25 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags,
next
end

unless feature_flag.key?(:impressionsDisabled)
feature_flag[:impressionsDisabled] = false
if config.debug_enabled
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
end
end
feature_flag = check_impressions_disabled(feature_flag, config)

config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
to_add.push(feature_flag)
end
feature_flag_repository.clear if clear_storage
feature_flag_repository.update(to_add, to_delete, change_number)
end

def self.check_impressions_disabled(feature_flag, config)
unless feature_flag.key?(:impressionsDisabled)
feature_flag[:impressionsDisabled] = false
if config.debug_enabled
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
end
end
feature_flag
end

def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config)
to_add = []
to_delete = []
Expand All @@ -39,6 +45,7 @@ def self.update_rule_based_segment_repository(rule_based_segment_repository, rul
config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled
to_add.push(rule_based_segment)
end

rule_based_segment_repository.update(to_add, to_delete, change_number)
end
end
Expand Down
14 changes: 13 additions & 1 deletion lib/splitclient-rb/helpers/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ class Util
def self.segment_names_by_object(object, matcher_type)
object[:conditions].each_with_object(Set.new) do |condition, names|
condition[:matcherGroup][:matchers].each do |matcher|
next if matcher[:userDefinedSegmentMatcherData].nil? or matcher[:matcherType] != matcher_type
next if matcher[:userDefinedSegmentMatcherData].nil? || matcher[:matcherType] != matcher_type

names << matcher[:userDefinedSegmentMatcherData][:segmentName]
end
end
end

def self.segment_names_in_rb_segment(object, matcher_type)
names = Set.new
names.merge segment_names_by_object(object, matcher_type)
object[:excluded][:segments].each do |segment|
if segment[:type] == 'standard'
names.add(segment[:name])
end
end
names
end
end
end
end
4 changes: 4 additions & 0 deletions lib/splitclient-rb/split_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,10 @@ def consumer?
@mode.equal?(:consumer)
end

def sdk_url_overriden?
return @base_uri != SplitConfig.default_base_uri
end

#
# gets the hostname where the sdk gem is running
#
Expand Down
2 changes: 1 addition & 1 deletion lib/splitclient-rb/sse/notification_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def process(incoming_notification)
private

def process_split_update(notification)
@config.logger.debug("#{notification.type} notification received: #{notification}") if @config.debug_enabled
@config.logger.debug("#{notification.event_type} notification received: #{notification}") if @config.debug_enabled
@splits_worker.add_to_queue(notification)
end

Expand Down
Loading