splitclient-rb 4.5.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/CHANGES.txt +147 -0
- data/Detailed-README.md +571 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/NEWS +75 -0
- data/README.md +43 -0
- data/Rakefile +24 -0
- data/exe/splitio +96 -0
- data/ext/murmurhash/MurmurHash3.java +162 -0
- data/lib/murmurhash/base.rb +58 -0
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/murmurhash/murmurhash_mri.rb +3 -0
- data/lib/splitclient-rb.rb +90 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +133 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +44 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +165 -0
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +30 -0
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +29 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +41 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +49 -0
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +78 -0
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +21 -0
- data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +129 -0
- data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +98 -0
- data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +22 -0
- data/lib/splitclient-rb/cache/repositories/repository.rb +23 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +82 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +106 -0
- data/lib/splitclient-rb/cache/routers/impression_router.rb +52 -0
- data/lib/splitclient-rb/cache/senders/events_sender.rb +47 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +73 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +67 -0
- data/lib/splitclient-rb/cache/senders/metrics_sender.rb +49 -0
- data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +48 -0
- data/lib/splitclient-rb/cache/stores/segment_store.rb +82 -0
- data/lib/splitclient-rb/cache/stores/split_store.rb +97 -0
- data/lib/splitclient-rb/clients/localhost_split_client.rb +92 -0
- data/lib/splitclient-rb/clients/split_client.rb +214 -0
- data/lib/splitclient-rb/engine/api/client.rb +74 -0
- data/lib/splitclient-rb/engine/api/events.rb +48 -0
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +55 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +42 -0
- data/lib/splitclient-rb/engine/api/metrics.rb +61 -0
- data/lib/splitclient-rb/engine/api/segments.rb +62 -0
- data/lib/splitclient-rb/engine/api/splits.rb +60 -0
- data/lib/splitclient-rb/engine/evaluator/splitter.rb +123 -0
- data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +46 -0
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +56 -0
- data/lib/splitclient-rb/engine/matchers/combiners.rb +9 -0
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +86 -0
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +21 -0
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +19 -0
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +30 -0
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +27 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +54 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +19 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +53 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +53 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +24 -0
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +60 -0
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +23 -0
- data/lib/splitclient-rb/engine/matchers/set_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +45 -0
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +128 -0
- data/lib/splitclient-rb/engine/metrics/metrics.rb +83 -0
- data/lib/splitclient-rb/engine/models/label.rb +8 -0
- data/lib/splitclient-rb/engine/models/split.rb +17 -0
- data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +210 -0
- data/lib/splitclient-rb/engine/parser/evaluator.rb +118 -0
- data/lib/splitclient-rb/engine/parser/partition.rb +35 -0
- data/lib/splitclient-rb/engine/parser/split_adapter.rb +88 -0
- data/lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb +4 -0
- data/lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb +4 -0
- data/lib/splitclient-rb/localhost_split_factory.rb +13 -0
- data/lib/splitclient-rb/localhost_utils.rb +36 -0
- data/lib/splitclient-rb/managers/localhost_split_manager.rb +45 -0
- data/lib/splitclient-rb/managers/split_manager.rb +77 -0
- data/lib/splitclient-rb/split_config.rb +391 -0
- data/lib/splitclient-rb/split_factory.rb +35 -0
- data/lib/splitclient-rb/split_factory_builder.rb +16 -0
- data/lib/splitclient-rb/utilitites.rb +41 -0
- data/lib/splitclient-rb/version.rb +3 -0
- data/splitclient-rb.gemspec +50 -0
- data/splitio.yml.example +7 -0
- data/tasks/benchmark_get_treatment.rake +43 -0
- data/tasks/irb.rake +4 -0
- data/tasks/rspec.rake +3 -0
- metadata +321 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# class to handle cached metrics
|
|
5
|
+
#
|
|
6
|
+
class Metrics < NoMethodError
|
|
7
|
+
|
|
8
|
+
@counter
|
|
9
|
+
@delta
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# cached latencies
|
|
13
|
+
#
|
|
14
|
+
# @return [object] array of latencies
|
|
15
|
+
attr_accessor :latencies
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# cached counts
|
|
19
|
+
#
|
|
20
|
+
# @return [object] array of counts
|
|
21
|
+
attr_accessor :counts
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# cached gauges
|
|
25
|
+
#
|
|
26
|
+
# @return [object] array of gauges
|
|
27
|
+
attr_accessor :gauges
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# quese size for cached arrays
|
|
31
|
+
#
|
|
32
|
+
# @return [int] queue size
|
|
33
|
+
attr_accessor :queue_size
|
|
34
|
+
|
|
35
|
+
def initialize(queue_size, config, repository)
|
|
36
|
+
@queue_size = queue_size
|
|
37
|
+
@binary_search = SplitIoClient::BinarySearchLatencyTracker.new
|
|
38
|
+
|
|
39
|
+
@config = config
|
|
40
|
+
|
|
41
|
+
@repository = repository
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# creates a new entry in the array for cached counts
|
|
46
|
+
#
|
|
47
|
+
# @param counter [string] name of the counter
|
|
48
|
+
# @delta [int] value of the counter
|
|
49
|
+
#
|
|
50
|
+
# @return void
|
|
51
|
+
def count(counter, delta)
|
|
52
|
+
return if (delta <= 0) || counter.nil? || counter.strip.empty?
|
|
53
|
+
|
|
54
|
+
@repository.add_count(counter, delta)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# creates a new entry in the array for cached time metrics
|
|
59
|
+
#
|
|
60
|
+
# @param operation [string] name of the operation
|
|
61
|
+
# @time_in_ms [number] time in miliseconds
|
|
62
|
+
#
|
|
63
|
+
# @return void
|
|
64
|
+
def time(operation, time_in_ms)
|
|
65
|
+
return if operation.nil? || operation.empty? || time_in_ms < 0
|
|
66
|
+
|
|
67
|
+
@repository.add_latency(operation, time_in_ms, @binary_search)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# creates a new entry in the array for cached gauges
|
|
72
|
+
#
|
|
73
|
+
# @param gauge [string] name of the gauge
|
|
74
|
+
# @value [number] value of the gauge
|
|
75
|
+
#
|
|
76
|
+
# @return void
|
|
77
|
+
def gauge(gauge, value)
|
|
78
|
+
return if gauge.nil? || gauge.empty?
|
|
79
|
+
|
|
80
|
+
@repository.add_gauge(gauge, value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class SplitIoClient::Engine::Models::Label
|
|
2
|
+
ARCHIVED = 'archived'.freeze
|
|
3
|
+
NO_RULE_MATCHED = 'default rule'.freeze
|
|
4
|
+
EXCEPTION = 'exception'.freeze
|
|
5
|
+
KILLED = 'killed'.freeze
|
|
6
|
+
NOT_IN_SPLIT = 'not in split'.freeze
|
|
7
|
+
DEFINITION_NOT_FOUND = 'definition not found'.freeze
|
|
8
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Engine
|
|
3
|
+
module Models
|
|
4
|
+
class Split
|
|
5
|
+
class << self
|
|
6
|
+
def matchable?(data)
|
|
7
|
+
data && data[:status] == 'ACTIVE' && data[:killed] == false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def archived?(data)
|
|
11
|
+
data && data[:status] == 'ARCHIVED'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
#
|
|
3
|
+
# acts as dto for a condition structure
|
|
4
|
+
#
|
|
5
|
+
class Condition < NoMethodError
|
|
6
|
+
TYPE_ROLLOUT = 'ROLLOUT'.freeze
|
|
7
|
+
TYPE_WHITELIST = 'WHITELIST'.freeze
|
|
8
|
+
#
|
|
9
|
+
# definition of the condition
|
|
10
|
+
#
|
|
11
|
+
# @returns [object] condition values
|
|
12
|
+
attr_accessor :data
|
|
13
|
+
|
|
14
|
+
def initialize(condition)
|
|
15
|
+
@data = condition
|
|
16
|
+
@partitions = set_partitions
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_condition_matcher(matchers)
|
|
20
|
+
CombiningMatcher.new(combiner, matchers) if combiner
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# @return [object] the combiner value for this condition
|
|
25
|
+
def combiner
|
|
26
|
+
@data[:matcherGroup][:combiner]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# @return [object] the matcher value for this condition
|
|
31
|
+
def matcher
|
|
32
|
+
@data[:matcherGroup][:matchers].first[:matcherType]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# @return [object] the matchers array value for this condition
|
|
37
|
+
def matchers
|
|
38
|
+
@data[:matcherGroup][:matchers]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# @return [string] condition type
|
|
43
|
+
def type
|
|
44
|
+
@data[:conditionType]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def negation_matcher(matcher)
|
|
48
|
+
NegationMatcher.new(matcher)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def matcher_all_keys(_params)
|
|
52
|
+
@matcher_all_keys ||= AllKeysMatcher.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# returns UserDefinedSegmentMatcher[object]
|
|
56
|
+
def matcher_in_segment(params)
|
|
57
|
+
matcher = params[:matcher]
|
|
58
|
+
segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName]
|
|
59
|
+
|
|
60
|
+
UserDefinedSegmentMatcher.new(params[:segments_repository], segment_name)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# returns WhitelistMatcher[object] the whitelist for this condition in case it has a whitelist matcher
|
|
64
|
+
def matcher_whitelist(params)
|
|
65
|
+
result = nil
|
|
66
|
+
matcher = params[:matcher]
|
|
67
|
+
is_user_whitelist = ((matcher[:keySelector]).nil? || (matcher[:keySelector])[:attribute].nil?)
|
|
68
|
+
if is_user_whitelist
|
|
69
|
+
result = (matcher[:whitelistMatcherData])[:whitelist]
|
|
70
|
+
else
|
|
71
|
+
attribute = (matcher[:keySelector])[:attribute]
|
|
72
|
+
white_list = (matcher[:whitelistMatcherData])[:whitelist]
|
|
73
|
+
result = { attribute: attribute, value: white_list }
|
|
74
|
+
end
|
|
75
|
+
WhitelistMatcher.new(result)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def matcher_equal_to(params)
|
|
79
|
+
matcher = params[:matcher]
|
|
80
|
+
attribute = (matcher[:keySelector])[:attribute]
|
|
81
|
+
value = (matcher[:unaryNumericMatcherData])[:value]
|
|
82
|
+
data_type = (matcher[:unaryNumericMatcherData])[:dataType]
|
|
83
|
+
EqualToMatcher.new(attribute: attribute, value: value, data_type: data_type)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def matcher_greater_than_or_equal_to(params)
|
|
87
|
+
matcher = params[:matcher]
|
|
88
|
+
attribute = (matcher[:keySelector])[:attribute]
|
|
89
|
+
value = (matcher[:unaryNumericMatcherData])[:value]
|
|
90
|
+
data_type = (matcher[:unaryNumericMatcherData])[:dataType]
|
|
91
|
+
GreaterThanOrEqualToMatcher.new(attribute: attribute, value: value, data_type: data_type)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def matcher_less_than_or_equal_to(params)
|
|
95
|
+
matcher = params[:matcher]
|
|
96
|
+
attribute = (matcher[:keySelector])[:attribute]
|
|
97
|
+
value = (matcher[:unaryNumericMatcherData])[:value]
|
|
98
|
+
data_type = (matcher[:unaryNumericMatcherData])[:dataType]
|
|
99
|
+
LessThanOrEqualToMatcher.new(attribute: attribute, value: value, data_type: data_type)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def matcher_between(params)
|
|
103
|
+
matcher = params[:matcher]
|
|
104
|
+
attribute = (matcher[:keySelector])[:attribute]
|
|
105
|
+
start_value = (matcher[:betweenMatcherData])[:start]
|
|
106
|
+
end_value = (matcher[:betweenMatcherData])[:end]
|
|
107
|
+
data_type = (matcher[:betweenMatcherData])[:dataType]
|
|
108
|
+
BetweenMatcher.new(attribute: attribute, start_value: start_value, end_value: end_value, data_type: data_type)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def matcher_equal_to_set(params)
|
|
112
|
+
EqualToSetMatcher.new(
|
|
113
|
+
params[:matcher][:keySelector][:attribute],
|
|
114
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def matcher_contains_any_of_set(params)
|
|
119
|
+
ContainsAnyMatcher.new(
|
|
120
|
+
params[:matcher][:keySelector][:attribute],
|
|
121
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def matcher_contains_all_of_set(params)
|
|
126
|
+
ContainsAllMatcher.new(
|
|
127
|
+
params[:matcher][:keySelector][:attribute],
|
|
128
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def matcher_part_of_set(params)
|
|
133
|
+
PartOfSetMatcher.new(
|
|
134
|
+
params[:matcher][:keySelector][:attribute],
|
|
135
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def matcher_starts_with(params)
|
|
140
|
+
StartsWithMatcher.new(
|
|
141
|
+
params[:matcher][:keySelector][:attribute],
|
|
142
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def matcher_ends_with(params)
|
|
147
|
+
EndsWithMatcher.new(
|
|
148
|
+
params[:matcher][:keySelector][:attribute],
|
|
149
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def matcher_contains_string(params)
|
|
154
|
+
ContainsMatcher.new(
|
|
155
|
+
params[:matcher][:keySelector][:attribute],
|
|
156
|
+
params[:matcher][:whitelistMatcherData][:whitelist]
|
|
157
|
+
)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def matcher_in_split_treatment(params)
|
|
161
|
+
DependencyMatcher.new(
|
|
162
|
+
params[:matcher][:dependencyMatcherData][:split],
|
|
163
|
+
params[:matcher][:dependencyMatcherData][:treatments]
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def matcher_equal_to_boolean(params)
|
|
168
|
+
EqualToBooleanMatcher.new(
|
|
169
|
+
params[:matcher][:keySelector][:attribute],
|
|
170
|
+
params[:matcher][:booleanMatcherData]
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def matcher_matches_string(params)
|
|
175
|
+
MatchesStringMatcher.new(
|
|
176
|
+
params[:matcher][:keySelector][:attribute],
|
|
177
|
+
params[:matcher][:stringMatcherData]
|
|
178
|
+
)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
#
|
|
182
|
+
# @return [object] the negate value for this condition
|
|
183
|
+
def negate
|
|
184
|
+
@data[:matcherGroup][:matchers].first[:negate]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
#
|
|
188
|
+
# @return [object] the array of partitions for this condition
|
|
189
|
+
attr_reader :partitions
|
|
190
|
+
|
|
191
|
+
#
|
|
192
|
+
# converts the partitions hash for this condition into an array of partition objects
|
|
193
|
+
#
|
|
194
|
+
# @return [void]
|
|
195
|
+
def set_partitions
|
|
196
|
+
partitions_list = []
|
|
197
|
+
@data[:partitions].each do |p|
|
|
198
|
+
partition = SplitIoClient::Partition.new(p)
|
|
199
|
+
partitions_list << partition
|
|
200
|
+
end
|
|
201
|
+
partitions_list
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
#
|
|
205
|
+
# @return [boolean] true if the condition is empty false otherwise
|
|
206
|
+
def empty?
|
|
207
|
+
@data.empty?
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Engine
|
|
3
|
+
module Parser
|
|
4
|
+
class Evaluator
|
|
5
|
+
def initialize(segments_repository, splits_repository, multiple = false)
|
|
6
|
+
@splits_repository = splits_repository
|
|
7
|
+
@segments_repository = segments_repository
|
|
8
|
+
@multiple = multiple
|
|
9
|
+
@cache = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(keys, split, attributes = nil)
|
|
13
|
+
# DependencyMatcher here, split is actually a split_name in this case
|
|
14
|
+
cache_result = split.is_a? String
|
|
15
|
+
split = @splits_repository.get_split(split) if cache_result
|
|
16
|
+
digest = Digest::MD5.hexdigest("#{{matching_key: keys[:matching_key]}}#{split}#{attributes}")
|
|
17
|
+
|
|
18
|
+
if Models::Split.archived?(split)
|
|
19
|
+
return treatment_hash(Models::Label::ARCHIVED, Models::Treatment::CONTROL, split[:changeNumber])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
treatment = if Models::Split.matchable?(split)
|
|
23
|
+
if @multiple && @cache[digest]
|
|
24
|
+
@cache[digest]
|
|
25
|
+
else
|
|
26
|
+
match(split, keys, attributes)
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
treatment_hash(Models::Label::KILLED, split[:defaultTreatment], split[:changeNumber])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@cache[digest] = treatment if cache_result
|
|
33
|
+
|
|
34
|
+
treatment
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def match(split, keys, attributes)
|
|
40
|
+
in_rollout = false
|
|
41
|
+
key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
|
|
42
|
+
legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false
|
|
43
|
+
splitter = Splitter.new
|
|
44
|
+
|
|
45
|
+
split[:conditions].each do |c|
|
|
46
|
+
condition = SplitIoClient::Condition.new(c)
|
|
47
|
+
|
|
48
|
+
next if condition.empty?
|
|
49
|
+
|
|
50
|
+
if !in_rollout && condition.type == SplitIoClient::Condition::TYPE_ROLLOUT
|
|
51
|
+
if split[:trafficAllocation] < 100
|
|
52
|
+
bucket = splitter.bucket(splitter.count_hash(key, split[:trafficAllocationSeed].to_i, legacy_algo))
|
|
53
|
+
|
|
54
|
+
if bucket >= split[:trafficAllocation]
|
|
55
|
+
return treatment_hash(Models::Label::NOT_IN_SPLIT, split[:defaultTreatment], split[:changeNumber])
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
in_rollout = true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
condition_matched = matcher_type(condition).match?(
|
|
63
|
+
matching_key: keys[:matching_key],
|
|
64
|
+
bucketing_key: keys[:bucketing_key],
|
|
65
|
+
evaluator: self,
|
|
66
|
+
attributes: attributes
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
next unless condition_matched
|
|
70
|
+
|
|
71
|
+
result = splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
|
|
72
|
+
|
|
73
|
+
if result.nil?
|
|
74
|
+
return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber])
|
|
75
|
+
else
|
|
76
|
+
return treatment_hash(c[:label], result, split[:changeNumber])
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def matcher_type(condition)
|
|
84
|
+
matchers = []
|
|
85
|
+
|
|
86
|
+
@segments_repository.adapter.pipelined do
|
|
87
|
+
condition.matchers.each do |matcher|
|
|
88
|
+
matchers << if matcher[:negate]
|
|
89
|
+
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher))
|
|
90
|
+
else
|
|
91
|
+
matcher_instance(matcher[:matcherType], condition, matcher)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
final_matcher = condition.create_condition_matcher(matchers)
|
|
97
|
+
|
|
98
|
+
if final_matcher.nil?
|
|
99
|
+
@logger.error('Invalid matcher type')
|
|
100
|
+
else
|
|
101
|
+
final_matcher
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def treatment_hash(label, treatment, change_number = nil)
|
|
106
|
+
{ label: label, treatment: treatment, change_number: change_number }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def matcher_instance(type, condition, matcher)
|
|
110
|
+
condition.send(
|
|
111
|
+
"matcher_#{type.downcase}",
|
|
112
|
+
matcher: matcher, segments_repository: @segments_repository
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
#
|
|
3
|
+
# acts as dto for a partition structure
|
|
4
|
+
#
|
|
5
|
+
class Partition < NoMethodError
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# definition of the condition
|
|
9
|
+
#
|
|
10
|
+
# @returns [object] condition values
|
|
11
|
+
attr_accessor :data
|
|
12
|
+
|
|
13
|
+
def initialize(partition)
|
|
14
|
+
@data = partition
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# @return [object] the treatment value for this partition
|
|
19
|
+
def treatment
|
|
20
|
+
@data[:treatment]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# @return [object] the size value for this partition
|
|
25
|
+
def size
|
|
26
|
+
@data[:size]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# @return [boolean] true if the partition is empty false otherwise
|
|
31
|
+
def is_empty?
|
|
32
|
+
@data.empty?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|