Skip to content

Commit 578cb0c

Browse files
authored
Merge pull request #552 from splitio/rbs-old-spec
Added old spec support
2 parents 5ac3978 + dcc45ce commit 578cb0c

File tree

17 files changed

+391
-153
lines changed

17 files changed

+391
-153
lines changed

lib/splitclient-rb/cache/fetchers/split_fetcher.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def call
2525
def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
2626
@semaphore.synchronize do
2727
data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options)
28-
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config)
28+
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage)
2929
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config)
3030
@splits_repository.set_segment_names(data[:segment_names])
3131
@rule_based_segments_repository.set_segment_names(data[:segment_names])

lib/splitclient-rb/cache/repositories/splits_repository.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ def initialize(config, flag_sets_repository, flag_set_filter)
4343
end
4444
@flag_sets = flag_sets_repository
4545
@flag_set_filter = flag_set_filter
46-
unless @config.mode.equal?(:consumer)
47-
@adapter.set_string(namespace_key('.splits.till'), '-1')
48-
@adapter.initialize_map(namespace_key('.segments.registered'))
49-
end
46+
initialize_keys
5047
end
5148

5249
def update(to_add, to_delete, new_change_number)
@@ -127,6 +124,7 @@ def clear
127124
@tt_cache.clear
128125

129126
@adapter.clear(namespace_key)
127+
initialize_keys
130128
end
131129

132130
def kill(change_number, split_name, default_treatment)
@@ -167,6 +165,13 @@ def flag_set_filter
167165

168166
private
169167

168+
def initialize_keys
169+
unless @config.mode.equal?(:consumer)
170+
@adapter.set_string(namespace_key('.splits.till'), '-1')
171+
@adapter.initialize_map(namespace_key('.segments.registered'))
172+
end
173+
end
174+
170175
def add_feature_flag(split)
171176
return unless split[:name]
172177
existing_split = get_split(split[:name])

lib/splitclient-rb/engine/api/client.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def post_api(url, api_key, data, headers = {}, params = {})
5050
raise e, 'Split SDK failed to connect to backend to post information', e.backtrace
5151
end
5252

53+
def sdk_url_overriden?
54+
@config.sdk_url_overriden?
55+
end
5356
private
5457

5558
def api_client

lib/splitclient-rb/engine/api/splits.rb

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,72 @@ module Api
55
# Retrieves split definitions from the Split Backend
66
class Splits < Client
77

8+
PROXY_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
9+
SPEC_1_1 = "1.1"
10+
811
def initialize(api_key, config, telemetry_runtime_producer)
912
super(config)
1013
@api_key = api_key
1114
@telemetry_runtime_producer = telemetry_runtime_producer
1215
@flag_sets_filter = @config.flag_sets_filter
16+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
17+
@last_proxy_check_timestamp = 0
18+
@clear_storage = false
19+
@old_spec_since = nil
1320
end
1421

1522
def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
1623
start = Time.now
24+
25+
if check_last_proxy_check_timestamp
26+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
27+
@config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.")
28+
@old_spec_since = since
29+
since = -1
30+
since_rbs = -1
31+
fetch_options = { cache_control_headers: false, till: nil, sets: nil}
32+
end
33+
34+
if @spec_version == Splits::SPEC_1_1
35+
since = @old_spec_since unless @old_spec_since.nil?
36+
params = { s: @spec_version, since: since }
37+
@old_spec_since = nil
38+
else
39+
params = { s: @spec_version, since: since, rbSince: since_rbs }
40+
end
1741

18-
params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs }
1942
params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
2043
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
2144
@config.logger.debug("Fetching from splitChanges with #{params}: ")
2245
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
46+
2347
if response.status == 414
2448
@config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
2549
raise ApiException.new response.body, 414
2650
end
51+
52+
if response.status == 400 and sdk_url_overriden? and @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
53+
@config.logger.warn("Detected proxy response error, changing spec version from #{@spec_version} to #{Splits::SPEC_1_1} and re-fetching.")
54+
@spec_version = Splits::SPEC_1_1
55+
@last_proxy_check_timestamp = Time.now
56+
return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till],
57+
sets: fetch_options[:sets]})
58+
end
59+
2760
if response.success?
28-
result = objects_with_segment_names(response.body)
61+
result = JSON.parse(response.body, symbolize_names: true)
62+
if @spec_version == Splits::SPEC_1_1
63+
result = convert_to_newSPEC(result)
64+
end
65+
66+
result[:rbs][:d] = check_rbs_data(result[:rbs][:d])
67+
result = objects_with_segment_names(result)
2968

69+
if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
70+
@clear_storage = @last_proxy_check_timestamp != 0
71+
@last_proxy_check_timestamp = 0
72+
end
73+
3074
unless result[:ff][:d].empty?
3175
@config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}")
3276
end
@@ -52,30 +96,42 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till
5296
end
5397
end
5498

99+
def clear_storage
100+
@clear_storage
101+
end
102+
55103
private
56104

57-
def objects_with_segment_names(objects_json)
58-
parsed_objects = JSON.parse(objects_json, symbolize_names: true)
105+
def check_rbs_data(rbs_data)
106+
rbs_data.each do |rb_segment|
107+
rb_segment[:excluded] = {:keys => [], :segments => []} if rb_segment[:excluded].nil?
108+
rb_segment[:excluded][:keys] = [] if rb_segment[:excluded][:keys].nil?
109+
rb_segment[:excluded][:segments] = [] if rb_segment[:excluded][:segments].nil?
110+
end
111+
rbs_data
112+
end
113+
114+
def objects_with_segment_names(parsed_objects)
59115
parsed_objects[:segment_names] = Set.new
60116
parsed_objects[:segment_names] =
61117
parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits|
62118
splits << Helpers::Util.segment_names_by_object(split, "IN_SEGMENT")
63119
end.flatten
64120

65121
parsed_objects[:rbs][:d].each do |rule_based_segment|
66-
parsed_objects[:segment_names].merge Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT")
67-
end
68-
69-
parsed_objects[:rbs][:d].each do |rule_based_segment|
70-
rule_based_segment[:excluded][:segments].each do |segment|
71-
if segment[:type] == "standard"
72-
parsed_objects[:segment_names].add(segment[:name])
73-
end
74-
end
122+
parsed_objects[:segment_names].merge Helpers::Util.segment_names_in_rb_segment(rule_based_segment, "IN_SEGMENT")
75123
end
76124

77125
parsed_objects
78126
end
127+
128+
def check_last_proxy_check_timestamp
129+
@spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS)
130+
end
131+
132+
def convert_to_newSPEC(body)
133+
{:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}}
134+
end
79135
end
80136
end
81137
end

lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,10 @@ def match?(args)
2525
rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name)
2626
return false if rule_based_segment.nil?
2727

28-
if args[:value].nil? or args[:value].empty?
29-
key = args[:matching_key]
30-
else
31-
key = args[:value]
32-
end
28+
key = update_key(args)
3329
return false if rule_based_segment[:excluded][:keys].include?(key)
3430

35-
rule_based_segment[:excluded][:segments].each do |segment|
36-
return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], key)
37-
38-
if segment[:type] == 'rule-based'
39-
return false if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args)
40-
end
41-
end
31+
return false unless check_excluded_segments(rule_based_segment, key, args)
4232

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

5343
private
5444

55-
def match_rbs(rule_based_segment, args)
56-
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, rule_based_segment[:name], @config)
57-
return rbs_matcher.match?(
58-
matching_key: args[:matching_key],
59-
bucketing_key: args[:value],
60-
attributes: args[:attributes]
61-
)
45+
def check_excluded_segments(rule_based_segment, key, args)
46+
rule_based_segment[:excluded][:segments].each do |segment|
47+
return false if segment[:type] == 'standard' && @segments_repository.in_segment?(segment[:name], key)
48+
49+
return false if segment[:type] == 'rule-based' && match_rbs(
50+
@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args
51+
)
52+
end
53+
true
54+
end
55+
56+
def update_key(args)
57+
if args[:value].nil? || args[:value].empty?
58+
args[:matching_key]
59+
else
60+
args[:value]
61+
end
62+
end
63+
64+
def match_rbs(rule_based_segment, args)
65+
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository,
66+
rule_based_segment[:name], @config)
67+
rbs_matcher.match?(matching_key: args[:matching_key],
68+
bucketing_key: args[:value],
69+
attributes: args[:attributes])
6270
end
6371
end
6472
end

lib/splitclient-rb/helpers/evaluator_helper.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ def self.matcher_type(condition, segments_repository, rb_segment_repository)
88
segments_repository.adapter.pipelined do
99
condition.matchers.each do |matcher|
1010
matchers << if matcher[:negate]
11-
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository))
11+
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition,
12+
matcher, segments_repository,
13+
rb_segment_repository))
1214
else
1315
matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)
1416
end

lib/splitclient-rb/helpers/repository_helper.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module SplitIoClient
44
module Helpers
55
class RepositoryHelper
6-
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config)
6+
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage)
77
to_add = []
88
to_delete = []
99
feature_flags.each do |feature_flag|
@@ -13,19 +13,25 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags,
1313
next
1414
end
1515

16-
unless feature_flag.key?(:impressionsDisabled)
17-
feature_flag[:impressionsDisabled] = false
18-
if config.debug_enabled
19-
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
20-
end
21-
end
16+
feature_flag = check_impressions_disabled(feature_flag, config)
2217

2318
config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
2419
to_add.push(feature_flag)
2520
end
21+
feature_flag_repository.clear if clear_storage
2622
feature_flag_repository.update(to_add, to_delete, change_number)
2723
end
2824

25+
def self.check_impressions_disabled(feature_flag, config)
26+
unless feature_flag.key?(:impressionsDisabled)
27+
feature_flag[:impressionsDisabled] = false
28+
if config.debug_enabled
29+
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
30+
end
31+
end
32+
feature_flag
33+
end
34+
2935
def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config)
3036
to_add = []
3137
to_delete = []
@@ -39,6 +45,7 @@ def self.update_rule_based_segment_repository(rule_based_segment_repository, rul
3945
config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled
4046
to_add.push(rule_based_segment)
4147
end
48+
4249
rule_based_segment_repository.update(to_add, to_delete, change_number)
4350
end
4451
end

lib/splitclient-rb/helpers/util.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,23 @@ class Util
66
def self.segment_names_by_object(object, matcher_type)
77
object[:conditions].each_with_object(Set.new) do |condition, names|
88
condition[:matcherGroup][:matchers].each do |matcher|
9-
next if matcher[:userDefinedSegmentMatcherData].nil? or matcher[:matcherType] != matcher_type
9+
next if matcher[:userDefinedSegmentMatcherData].nil? || matcher[:matcherType] != matcher_type
10+
1011
names << matcher[:userDefinedSegmentMatcherData][:segmentName]
1112
end
1213
end
1314
end
15+
16+
def self.segment_names_in_rb_segment(object, matcher_type)
17+
names = Set.new
18+
names.merge segment_names_by_object(object, matcher_type)
19+
object[:excluded][:segments].each do |segment|
20+
if segment[:type] == 'standard'
21+
names.add(segment[:name])
22+
end
23+
end
24+
names
25+
end
1426
end
1527
end
1628
end

lib/splitclient-rb/split_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,10 @@ def consumer?
645645
@mode.equal?(:consumer)
646646
end
647647

648+
def sdk_url_overriden?
649+
return @base_uri != SplitConfig.default_base_uri
650+
end
651+
648652
#
649653
# gets the hostname where the sdk gem is running
650654
#

lib/splitclient-rb/sse/notification_processor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def process(incoming_notification)
2727
private
2828

2929
def process_split_update(notification)
30-
@config.logger.debug("#{notification.type} notification received: #{notification}") if @config.debug_enabled
30+
@config.logger.debug("#{notification.event_type} notification received: #{notification}") if @config.debug_enabled
3131
@splits_worker.add_to_queue(notification)
3232
end
3333

0 commit comments

Comments
 (0)