splitclient-rb 8.1.3.pre.rc4-java → 8.3.0.pre.rc1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/CHANGES.txt +3 -0
  4. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +2 -29
  5. data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
  6. data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
  7. data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
  8. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +100 -39
  9. data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +1 -1
  10. data/lib/splitclient-rb/clients/split_client.rb +165 -81
  11. data/lib/splitclient-rb/engine/api/splits.rb +8 -3
  12. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +1 -1
  13. data/lib/splitclient-rb/engine/parser/evaluator.rb +15 -21
  14. data/lib/splitclient-rb/exceptions.rb +11 -0
  15. data/lib/splitclient-rb/helpers/repository_helper.rb +23 -0
  16. data/lib/splitclient-rb/split_config.rb +22 -6
  17. data/lib/splitclient-rb/split_factory.rb +32 -9
  18. data/lib/splitclient-rb/sse/workers/splits_worker.rb +2 -9
  19. data/lib/splitclient-rb/telemetry/domain/constants.rb +4 -0
  20. data/lib/splitclient-rb/telemetry/domain/structs.rb +4 -4
  21. data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +18 -2
  22. data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +0 -1
  23. data/lib/splitclient-rb/telemetry/storages/memory.rb +12 -0
  24. data/lib/splitclient-rb/telemetry/synchronizer.rb +6 -2
  25. data/lib/splitclient-rb/validators.rb +64 -3
  26. data/lib/splitclient-rb/version.rb +1 -1
  27. data/lib/splitclient-rb.rb +4 -0
  28. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8753013ccb2e60edf514115dd808c531a55f0bb0
4
- data.tar.gz: 258ea4f4543ca8a977f0e662fd4f156cf3a39698
3
+ metadata.gz: 63eaef95e83fc3552686629bbc774714697479d3
4
+ data.tar.gz: 0a749fd03518f96e809823ba07ea2dedb2aafd8c
5
5
  SHA512:
6
- metadata.gz: 1869e634836e8fd54847aaa7b03a9d916f1650a616bb4da85cd7b56ff0879b5af3b03f07da66a1717e663d823b10d3e50d0245aeb66e3e727fdff8bd71f78ea5
7
- data.tar.gz: f6ba403082dc072594565f22785c2c3cbcc8c7b027716570e0321ac28a6591a0b4348de0bac969bce84caf480964902b755364afeda3b8e5d22f1e79c62f2c8f
6
+ metadata.gz: 7c6f40b8f8fec08b07123de1c441da6a829847a0933993b74e156d143d281f4966a93cbf2c4960d1f0af1035042e35e85caf9cf79dda3a228cb4e2e253b9404b
7
+ data.tar.gz: f992ed57b0bf38032e2364a15ea9e1a3bb2bdfc6ac9d09582bad0d939aa993400476e5ace9993b1aaa44843b8f09c8d4cdaa8e86485e2ff5b240d1e7fdd9ba8e
@@ -0,0 +1 @@
1
+ * @splitio/sdk
data/CHANGES.txt CHANGED
@@ -1,5 +1,8 @@
1
1
  CHANGES
2
2
 
3
+ 8.2.0 (Jul 18, 2023)
4
+ - Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system.
5
+
3
6
  8.1.2 (May 15, 2023)
4
7
  - Updated terminology on the SDKs codebase to be more aligned with current standard without causing a breaking change. The core change is the term split for feature flag on things like logs and IntelliSense comments.
5
8
 
@@ -17,7 +17,7 @@ module SplitIoClient
17
17
  fetch_splits
18
18
  return
19
19
  end
20
-
20
+
21
21
  splits_thread
22
22
  end
23
23
 
@@ -25,13 +25,8 @@ module SplitIoClient
25
25
  @semaphore.synchronize do
26
26
  data = splits_since(@splits_repository.get_change_number, fetch_options)
27
27
 
28
- data[:splits] && data[:splits].each do |split|
29
- add_split_unless_archived(split)
30
- end
31
-
28
+ SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:splits], data[:till], @config)
32
29
  @splits_repository.set_segment_names(data[:segment_names])
33
- @splits_repository.set_change_number(data[:till])
34
-
35
30
  @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
36
31
 
37
32
  { segment_names: data[:segment_names], success: true }
@@ -64,28 +59,6 @@ module SplitIoClient
64
59
  splits_api.since(since, fetch_options)
65
60
  end
66
61
 
67
- def add_split_unless_archived(split)
68
- if Engine::Models::Split.archived?(split)
69
- @config.logger.debug("Seeing archived feature flag #{split[:name]}") if @config.debug_enabled
70
-
71
- remove_archived_split(split)
72
- else
73
- store_split(split)
74
- end
75
- end
76
-
77
- def remove_archived_split(split)
78
- @config.logger.debug("removing feature flag from store(#{split})") if @config.debug_enabled
79
-
80
- @splits_repository.remove_split(split)
81
- end
82
-
83
- def store_split(split)
84
- @config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled
85
-
86
- @splits_repository.add_split(split)
87
- end
88
-
89
62
  def splits_api
90
63
  @splits_api ||= SplitIoClient::Api::Splits.new(@api_key, @config, @telemetry_runtime_producer)
91
64
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module SplitIoClient
6
+ module Cache
7
+ module Filter
8
+ class FlagSetsFilter
9
+ def initialize(flag_sets = [])
10
+ @flag_sets = Set.new(flag_sets)
11
+ @should_filter = @flag_sets.any?
12
+ end
13
+
14
+ def should_filter?
15
+ @should_filter
16
+ end
17
+
18
+ def flag_set_exist?(flag_set)
19
+ return true unless @should_filter
20
+
21
+ if not flag_set.is_a?(String) or flag_set.empty?
22
+ return false
23
+ end
24
+
25
+ @flag_sets.intersection([flag_set]).any?
26
+ end
27
+
28
+ def intersect?(flag_sets)
29
+ return true unless @should_filter
30
+
31
+ if not flag_sets.is_a?(Array) or flag_sets.empty?
32
+ return false
33
+ end
34
+
35
+ @flag_sets.intersection(Set.new(flag_sets)).any?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class MemoryFlagSetsRepository
7
+ def initialize(flag_sets = [])
8
+ @sets_feature_flag_map = {}
9
+ flag_sets.each{ |flag_set| @sets_feature_flag_map[flag_set] = Set[] }
10
+ end
11
+
12
+ def flag_set_exist?(flag_set)
13
+ @sets_feature_flag_map.key?(flag_set)
14
+ end
15
+
16
+ def get_flag_sets(flag_sets)
17
+ to_return = Array.new
18
+ flag_sets.each { |flag_set| to_return.concat(@sets_feature_flag_map[flag_set].to_a)}
19
+ to_return.uniq
20
+ end
21
+
22
+ def add_flag_set(flag_set)
23
+ @sets_feature_flag_map[flag_set] = Set[] if !flag_set_exist?(flag_set)
24
+ end
25
+
26
+ def remove_flag_set(flag_set)
27
+ @sets_feature_flag_map.delete(flag_set) if flag_set_exist?(flag_set)
28
+ end
29
+
30
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
31
+ @sets_feature_flag_map[flag_set].add(feature_flag) if flag_set_exist?(flag_set)
32
+ end
33
+
34
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
35
+ @sets_feature_flag_map[flag_set].delete(feature_flag) if flag_set_exist?(flag_set)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class RedisFlagSetsRepository < Repository
7
+
8
+ def initialize(config)
9
+ super(config)
10
+ @adapter = SplitIoClient::Cache::Adapters::RedisAdapter.new(@config.redis_url)
11
+ end
12
+
13
+ def flag_set_exist?(flag_set)
14
+ @adapter.exists?(namespace_key(".flagSet.#{flag_set}"))
15
+ end
16
+
17
+ def get_flag_sets(flag_sets)
18
+ result = @adapter.redis.pipelined do |pipeline|
19
+ flag_sets.each do |flag_set|
20
+ pipeline.smembers(namespace_key(".flagSet.#{flag_set}"))
21
+ end
22
+ end
23
+ to_return = Array.new
24
+ result.each do |flag_set|
25
+ flag_set.each { |feature_flag_name| to_return.push(feature_flag_name.to_s)}
26
+ end
27
+ to_return.uniq
28
+ end
29
+
30
+ def add_flag_set(flag_set)
31
+ # not implemented
32
+ end
33
+
34
+ def remove_flag_set(flag_set)
35
+ # not implemented
36
+ end
37
+
38
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
39
+ # not implemented
40
+ end
41
+
42
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
43
+ # not implemented
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,7 +6,7 @@ module SplitIoClient
6
6
  class SplitsRepository < Repository
7
7
  attr_reader :adapter
8
8
 
9
- def initialize(config)
9
+ def initialize(config, flag_sets_repository, flag_set_filter)
10
10
  super(config)
11
11
  @tt_cache = {}
12
12
  @adapter = case @config.cache_adapter.class.to_s
@@ -15,48 +15,18 @@ module SplitIoClient
15
15
  else
16
16
  @config.cache_adapter
17
17
  end
18
+ @flag_sets = flag_sets_repository
19
+ @flag_set_filter = flag_set_filter
18
20
  unless @config.mode.equal?(:consumer)
19
21
  @adapter.set_string(namespace_key('.splits.till'), '-1')
20
22
  @adapter.initialize_map(namespace_key('.segments.registered'))
21
23
  end
22
24
  end
23
25
 
24
- def add_split(split)
25
- return unless split[:name]
26
- existing_split = get_split(split[:name])
27
-
28
- if(!existing_split)
29
- increase_tt_name_count(split[:trafficTypeName])
30
- elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
31
- increase_tt_name_count(split[:trafficTypeName])
32
- decrease_tt_name_count(existing_split[:trafficTypeName])
33
- end
34
-
35
- @adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
36
- end
37
-
38
- def remove_split(split)
39
- tt_name = split[:trafficTypeName]
40
-
41
- decrease_tt_name_count(split[:trafficTypeName])
42
-
43
- @adapter.delete(namespace_key(".split.#{split[:name]}"))
44
- end
45
-
46
- def get_splits(names, symbolize_names = true)
47
- splits = {}
48
- split_names = names.map { |name| namespace_key(".split.#{name}") }
49
- splits.merge!(
50
- @adapter
51
- .multiple_strings(split_names)
52
- .map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
53
- )
54
-
55
- splits.map do |name, data|
56
- parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
57
- split_name = symbolize_names ? name.to_sym : name
58
- [split_name, parsed_data]
59
- end.to_h
26
+ def update(to_add, to_delete, new_change_number)
27
+ to_add.each{ |feature_flag| add_feature_flag(feature_flag) }
28
+ to_delete.each{ |feature_flag| remove_feature_flag(feature_flag) }
29
+ set_change_number(new_change_number)
60
30
  end
61
31
 
62
32
  def get_split(name)
@@ -65,8 +35,13 @@ module SplitIoClient
65
35
  JSON.parse(split, symbolize_names: true) if split
66
36
  end
67
37
 
68
- def splits
69
- get_splits(split_names, false)
38
+ def splits(filtered_names=nil)
39
+ symbolize = true
40
+ if filtered_names.nil?
41
+ filtered_names = split_names
42
+ symbolize = false
43
+ end
44
+ get_splits(filtered_names, symbolize)
70
45
  end
71
46
 
72
47
  def traffic_type_exists(tt_name)
@@ -144,8 +119,94 @@ module SplitIoClient
144
119
  split_names.length
145
120
  end
146
121
 
122
+ def get_feature_flags_by_sets(flag_sets)
123
+ sets_to_fetch = Array.new
124
+ flag_sets.each do |flag_set|
125
+ unless @flag_sets.flag_set_exist?(flag_set)
126
+ @config.logger.warn("Flag set #{flag_set} is not part of the configured flag set list, ignoring it.")
127
+ next
128
+ end
129
+ sets_to_fetch.push(flag_set)
130
+ end
131
+ @flag_sets.get_flag_sets(flag_sets)
132
+ end
133
+
134
+ def is_flag_set_exist(flag_set)
135
+ @flag_sets.flag_set_exist?(flag_set)
136
+ end
137
+
138
+ def flag_set_filter
139
+ @flag_set_filter
140
+ end
141
+
147
142
  private
148
143
 
144
+ def add_feature_flag(split)
145
+ return unless split[:name]
146
+ existing_split = get_split(split[:name])
147
+
148
+ if(!existing_split)
149
+ increase_tt_name_count(split[:trafficTypeName])
150
+ elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
151
+ increase_tt_name_count(split[:trafficTypeName])
152
+ decrease_tt_name_count(existing_split[:trafficTypeName])
153
+ remove_from_flag_sets(existing_split)
154
+ elsif(existing_split[:sets] != split[:sets])
155
+ remove_from_flag_sets(existing_split)
156
+ end
157
+
158
+ if !split[:sets].nil?
159
+ for flag_set in split[:sets]
160
+ if !@flag_sets.flag_set_exist?(flag_set)
161
+ if @flag_set_filter.should_filter?
162
+ next
163
+ end
164
+ @flag_sets.add_flag_set(flag_set)
165
+ end
166
+ @flag_sets.add_feature_flag_to_flag_set(flag_set, split[:name])
167
+ end
168
+ end
169
+
170
+ @adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
171
+ end
172
+
173
+ def remove_feature_flag(split)
174
+ tt_name = split[:trafficTypeName]
175
+
176
+ decrease_tt_name_count(split[:trafficTypeName])
177
+ remove_from_flag_sets(split)
178
+ @adapter.delete(namespace_key(".split.#{split[:name]}"))
179
+ end
180
+
181
+ def get_splits(names, symbolize_names = true)
182
+ splits = {}
183
+ split_names = names.map { |name| namespace_key(".split.#{name}") }
184
+ splits.merge!(
185
+ @adapter
186
+ .multiple_strings(split_names)
187
+ .map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
188
+ )
189
+
190
+ splits.map do |name, data|
191
+ parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
192
+ split_name = symbolize_names ? name.to_sym : name
193
+ [split_name, parsed_data]
194
+ end.to_h
195
+ end
196
+
197
+ def remove_from_flag_sets(feature_flag)
198
+ name = feature_flag[:name]
199
+ flag_sets = get_split(name)[:sets] if exists?(name)
200
+ if !flag_sets.nil?
201
+ for flag_set in flag_sets
202
+ @flag_sets.remove_feature_flag_from_flag_set(flag_set, feature_flag[:name])
203
+ if is_flag_set_exist(flag_set) && @flag_sets.get_flag_sets([flag_set]).length == 0 && !@flag_set_filter.should_filter?
204
+ @flag_sets.remove_flag_set(flag_set)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
149
210
  def increase_tt_name_count(tt_name)
150
211
  return unless tt_name
151
212
 
@@ -55,7 +55,7 @@ module SplitIoClient
55
55
  def store_split(split)
56
56
  @config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled
57
57
 
58
- @splits_repository.add_split(split)
58
+ @splits_repository.update([split], [], -1)
59
59
  end
60
60
 
61
61
  def load_features