splitclient-rb 3.1.2 → 3.1.3.pre.rc1
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 +4 -4
- data/CHANGES.txt +7 -0
- data/Detailed-README.md +20 -5
- data/exe/splitio +54 -3
- data/lib/cache/adapters/memory_adapters/map_adapter.rb +6 -0
- data/lib/cache/adapters/redis_adapter.rb +6 -1
- data/lib/cache/repositories/impressions/memory_repository.rb +8 -2
- data/lib/cache/repositories/impressions/redis_repository.rb +2 -2
- data/lib/cache/repositories/impressions_repository.rb +3 -0
- data/lib/cache/repositories/metrics_repository.rb +1 -0
- data/lib/cache/repositories/repository.rb +1 -1
- data/lib/cache/repositories/segments_repository.rb +16 -1
- data/lib/cache/repositories/splits_repository.rb +21 -7
- data/lib/cache/senders/impressions_sender.rb +16 -12
- data/lib/cache/senders/metrics_sender.rb +41 -0
- data/lib/cache/stores/sdk_blocker.rb +9 -8
- data/lib/cache/stores/segment_store.rb +1 -1
- data/lib/engine/api/client.rb +15 -2
- data/lib/engine/api/metrics.rb +57 -0
- data/lib/engine/parser/split_adapter.rb +9 -140
- data/lib/engine/parser/split_treatment.rb +7 -5
- data/lib/engine/partitions/treatments.rb +1 -5
- data/lib/splitclient-rb.rb +14 -4
- data/lib/splitclient-rb/clients/localhost_split_client.rb +89 -0
- data/lib/splitclient-rb/clients/split_client.rb +114 -0
- data/lib/splitclient-rb/localhost_split_factory.rb +6 -187
- 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 +20 -0
- data/lib/splitclient-rb/split_factory.rb +16 -217
- data/lib/splitclient-rb/split_factory_builder.rb +3 -2
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb_utilitites.rb +24 -19
- data/splitclient-rb.gemspec +1 -1
- data/{splitio.yml → splitio.yml.example} +0 -0
- metadata +26 -19
@@ -26,7 +26,7 @@ module SplitIoClient
|
|
26
26
|
|
27
27
|
def blocked_store
|
28
28
|
loop do
|
29
|
-
next unless @sdk_blocker.
|
29
|
+
next unless @sdk_blocker.splits_repository.ready?
|
30
30
|
|
31
31
|
store_segments
|
32
32
|
@config.logger.debug("Segment names: #{@segments_repository.used_segment_names.to_a}") if @config.debug_enabled
|
data/lib/engine/api/client.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'faraday/http_cache'
|
2
|
+
require 'bundler/vendor/net/http/persistent' unless defined?(Net::HTTP)
|
3
|
+
require 'faraday_middleware'
|
4
|
+
|
1
5
|
module SplitIoClient
|
2
6
|
module Api
|
3
7
|
class Client
|
@@ -41,11 +45,20 @@ module SplitIoClient
|
|
41
45
|
def common_headers(api_key, config)
|
42
46
|
{
|
43
47
|
'Authorization' => "Bearer #{api_key}",
|
44
|
-
'SplitSDKVersion' => SplitIoClient::
|
48
|
+
'SplitSDKVersion' => SplitIoClient::SplitConfig.sdk_version,
|
45
49
|
'SplitSDKMachineName' => config.machine_name,
|
46
|
-
'SplitSDKMachineIP' => config.machine_ip
|
50
|
+
'SplitSDKMachineIP' => config.machine_ip,
|
51
|
+
'Referer' => referer
|
47
52
|
}
|
48
53
|
end
|
54
|
+
|
55
|
+
def referer
|
56
|
+
result = SplitIoClient::SplitConfig.sdk_version
|
57
|
+
|
58
|
+
result = "#{result}::#{SplitIoClient::SplitConfig.get_hostname}" unless SplitIoClient::SplitConfig.get_hostname == 'localhost'
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
49
62
|
end
|
50
63
|
end
|
51
64
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Api
|
3
|
+
class Metrics < Client
|
4
|
+
def initialize(api_key, config, metrics_repository)
|
5
|
+
@config = config
|
6
|
+
@api_key = api_key
|
7
|
+
@metrics_repository = metrics_repository
|
8
|
+
end
|
9
|
+
|
10
|
+
def post
|
11
|
+
post_latencies
|
12
|
+
post_counts
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def post_latencies
|
18
|
+
if @metrics_repository.latencies.empty?
|
19
|
+
@config.logger.debug('No latencies to report.') if @config.debug_enabled
|
20
|
+
else
|
21
|
+
@metrics_repository.latencies.each do |name, latencies|
|
22
|
+
metrics_time = { name: name, latencies: latencies }
|
23
|
+
|
24
|
+
result = post_api("#{@config.events_uri}/metrics/time", @config, @api_key, metrics_time)
|
25
|
+
|
26
|
+
if result.status / 100 != 2
|
27
|
+
@config.logger.error("Unexpected status code while posting time metrics: #{result.status}")
|
28
|
+
else
|
29
|
+
@config.logger.debug("Metric time reported: #{metrics_time.size}") if @config.debug_enabled
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@metrics_repository.clear_latencies
|
35
|
+
end
|
36
|
+
|
37
|
+
def post_counts
|
38
|
+
if @metrics_repository.counts.empty?
|
39
|
+
@config.logger.debug('No counts to report.') if @config.debug_enabled
|
40
|
+
else
|
41
|
+
@metrics_repository.counts.each do |name, count|
|
42
|
+
metrics_count = { name: name, delta: count }
|
43
|
+
|
44
|
+
result = post_api("#{@config.events_uri}/metrics/counter", @config, @api_key, metrics_count)
|
45
|
+
|
46
|
+
if result.status / 100 != 2
|
47
|
+
@config.logger.error("Unexpected status code while posting count metrics: #{result.status}")
|
48
|
+
else
|
49
|
+
@config.logger.debug("Metric counts reported: #{metrics_count.size}") if @config.debug_enabled
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@metrics_repository.clear_counts
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,58 +1,37 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'thread'
|
3
|
-
require 'faraday/http_cache'
|
4
|
-
require 'bundler/vendor/net/http/persistent' unless defined?(Net::HTTP)
|
5
|
-
require 'faraday_middleware'
|
6
|
-
|
7
3
|
|
8
4
|
module SplitIoClient
|
9
|
-
|
10
5
|
#
|
11
6
|
# acts as an api adapater to connect to split endpoints
|
12
7
|
# uses a configuration object that can be modified when creating the client instance
|
13
8
|
# also, uses safe threads to execute fetches and post give the time execution values from the config
|
14
9
|
#
|
15
10
|
class SplitAdapter < NoMethodError
|
16
|
-
|
17
|
-
# handler for metrics
|
18
|
-
attr_reader :metrics
|
19
|
-
|
20
|
-
#
|
21
|
-
# handler for parsed splits
|
22
|
-
attr_reader :parsed_splits
|
23
|
-
|
24
|
-
#
|
25
|
-
# handeler for parsed segments
|
26
|
-
attr_reader :parsed_segments
|
27
|
-
|
28
|
-
attr_reader :impressions_producer
|
29
|
-
|
30
|
-
attr_reader :splits_repository, :segments_repository, :impressions_repository
|
11
|
+
attr_reader :splits_repository, :segments_repository, :impressions_repository, :metrics
|
31
12
|
|
32
13
|
#
|
33
14
|
# Creates a new split api adapter instance that consumes split api endpoints
|
34
15
|
#
|
35
16
|
# @param api_key [String] the API key for your split account
|
17
|
+
# @param config [SplitConfig] SplitConfig instance
|
18
|
+
# @param splits_repository [SplitsRepository] SplitsRepository instance to store splits in
|
19
|
+
# @param segments_repository [SegmentsRepository] SegmentsRepository instance to store segments in
|
20
|
+
# @param impressions_repository [ImpressionsRepository] ImpressionsRepository instance to store impressions in
|
21
|
+
# @param metrics_repository [MetricsRepository] SplitsRepository instance to store metrics in
|
22
|
+
# @param sdk_blocker [SDKBlocker] SDKBlocker instance which blocks splits_repository/segments_repository
|
36
23
|
#
|
37
24
|
# @return [SplitIoClient] split.io client instance
|
38
25
|
def initialize(api_key, config, splits_repository, segments_repository, impressions_repository, metrics_repository, sdk_blocker)
|
39
26
|
@api_key = api_key
|
40
27
|
@config = config
|
41
|
-
|
42
28
|
@splits_repository = splits_repository
|
43
29
|
@segments_repository = segments_repository
|
44
30
|
@impressions_repository = impressions_repository
|
45
31
|
@metrics_repository = metrics_repository
|
46
|
-
|
47
32
|
@metrics = Metrics.new(100, @config, @metrics_repository)
|
48
|
-
|
49
33
|
@sdk_blocker = sdk_blocker
|
50
34
|
|
51
|
-
@api_client = Faraday.new do |builder|
|
52
|
-
builder.use FaradayMiddleware::Gzip
|
53
|
-
builder.adapter :net_http_persistent
|
54
|
-
end
|
55
|
-
|
56
35
|
start_based_on_mode(@config.mode)
|
57
36
|
end
|
58
37
|
|
@@ -90,119 +69,9 @@ module SplitIoClient
|
|
90
69
|
SplitIoClient::Cache::Senders::ImpressionsSender.new(@impressions_repository, @config, @api_key).call
|
91
70
|
end
|
92
71
|
|
93
|
-
#
|
94
|
-
# helper method to execute a post request to the provided endpoint
|
95
|
-
#
|
96
|
-
# @param path [string] api endpoint path
|
97
|
-
# @param params [object] hash of params that will be added to the request
|
98
|
-
#
|
99
|
-
# @return [object] response to the request
|
100
|
-
def post_api(path, param)
|
101
|
-
@api_client.post (@config.events_uri + path) do |req|
|
102
|
-
req.headers['Authorization'] = 'Bearer ' + @api_key
|
103
|
-
req.headers['Content-Type'] = 'application/json'
|
104
|
-
req.headers['SplitSDKVersion'] = SplitIoClient::SplitFactory.sdk_version
|
105
|
-
req.headers['SplitSDKMachineName'] = @config.machine_name
|
106
|
-
req.headers['SplitSDKMachineIP'] = @config.machine_ip
|
107
|
-
req.body = param.to_json
|
108
|
-
req.options.timeout = @config.read_timeout
|
109
|
-
req.options.open_timeout = @config.connection_timeout
|
110
|
-
|
111
|
-
if @config.transport_debug_enabled
|
112
|
-
@config.logger.debug("POST #{@config.events_uri + path} #{req.body}")
|
113
|
-
elsif @config.debug_enabled
|
114
|
-
@config.logger.debug("POST #{@config.events_uri + path}")
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
#
|
121
|
-
# @return parsed_splits [object] parsed splits for this adapter
|
122
|
-
def parsed_splits
|
123
|
-
@parsed_splits
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# @return parsed_segments [object] parsed segments for this adapter
|
128
|
-
def parsed_segments
|
129
|
-
@parsed_segments
|
130
|
-
end
|
131
|
-
|
132
|
-
#
|
133
|
-
# creates two safe threads that will be executing api calls
|
134
|
-
# for posting impressions and metrics given the execution time
|
135
|
-
# provided within the configuration
|
136
|
-
#
|
137
|
-
|
72
|
+
# Starts thread which loops constantly and sends metrics to the Split API
|
138
73
|
def metrics_sender
|
139
|
-
|
140
|
-
return if ENV['SPLITCLIENT_ENV'] == 'test'
|
141
|
-
|
142
|
-
Thread.new do
|
143
|
-
|
144
|
-
@config.logger.info('Starting metrics service')
|
145
|
-
|
146
|
-
loop do
|
147
|
-
begin
|
148
|
-
|
149
|
-
post_metrics
|
150
|
-
|
151
|
-
rescue StandardError => error
|
152
|
-
@config.log_found_exception(__method__.to_s, error)
|
153
|
-
end
|
154
|
-
|
155
|
-
# Sleep either on success of failure.
|
156
|
-
sleep(randomize_interval(@config.metrics_refresh_rate))
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
#
|
163
|
-
# creates the appropriate json data for the cached metrics values
|
164
|
-
# include latencies, counts and gauges
|
165
|
-
# and then sends them to the appropriate api endpoint with a valida body format
|
166
|
-
#
|
167
|
-
# @return [void]
|
168
|
-
def post_metrics
|
169
|
-
if @metrics_repository.latencies.empty?
|
170
|
-
@config.logger.debug('No latencies to report.') if @config.debug_enabled
|
171
|
-
else
|
172
|
-
@metrics_repository.latencies.each do |name, latencies|
|
173
|
-
metrics_time = { name: name, latencies: latencies }
|
174
|
-
res = post_api('/metrics/time', metrics_time)
|
175
|
-
if res.status / 100 != 2
|
176
|
-
@config.logger.error("Unexpected status code while posting time metrics: #{res.status}")
|
177
|
-
else
|
178
|
-
@config.logger.debug("Metric time reported: #{metrics_time.size}") if @config.debug_enabled
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
@metrics_repository.clear_latencies
|
183
|
-
|
184
|
-
if @metrics_repository.counts.empty?
|
185
|
-
@config.logger.debug('No counts to report.') if @config.debug_enabled
|
186
|
-
else
|
187
|
-
@metrics_repository.counts.each do |name, count|
|
188
|
-
metrics_count = { name: name, delta: count }
|
189
|
-
res = post_api('/metrics/counter', metrics_count)
|
190
|
-
if res.status / 100 != 2
|
191
|
-
@config.logger.error("Unexpected status code while posting count metrics: #{res.status}")
|
192
|
-
else
|
193
|
-
@config.logger.debug("Metric counts reported: #{metrics_count.size}") if @config.debug_enabled
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
@metrics_repository.clear_counts
|
198
|
-
end
|
199
|
-
|
200
|
-
private
|
201
|
-
|
202
|
-
def randomize_interval(interval)
|
203
|
-
@random_generator ||= Random.new
|
204
|
-
random_factor = @random_generator.rand(50..100)/100.0
|
205
|
-
interval * random_factor
|
74
|
+
SplitIoClient::Cache::Senders::MetricsSender.new(@metrics_repository, @config, @api_key).call
|
206
75
|
end
|
207
76
|
end
|
208
77
|
end
|
@@ -36,11 +36,13 @@ module SplitIoClient
|
|
36
36
|
def matcher_type(condition)
|
37
37
|
matchers = []
|
38
38
|
|
39
|
-
|
40
|
-
matchers
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
@segments_repository.adapter.pipelined do
|
40
|
+
condition.matchers.each do |matcher|
|
41
|
+
matchers << condition.send(
|
42
|
+
"matcher_#{matcher[:matcherType].downcase}",
|
43
|
+
matcher: matcher, segments_repository: @segments_repository
|
44
|
+
)
|
45
|
+
end
|
44
46
|
end
|
45
47
|
|
46
48
|
final_matcher = condition.create_condition_matcher(matchers)
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module SplitIoClient
|
2
|
-
|
3
2
|
#
|
4
3
|
# represents the possible return values for a treatment
|
5
4
|
#
|
6
5
|
class Treatments < NoMethodError
|
7
|
-
|
8
6
|
# Constants to represent treatment values
|
9
7
|
CONTROL = 'control'
|
10
8
|
OFF = 'off'
|
@@ -34,7 +32,5 @@ module SplitIoClient
|
|
34
32
|
def self.is_control?(treatment)
|
35
33
|
get_type(treatment).equal?(CONTROL) ? true : false
|
36
34
|
end
|
37
|
-
|
38
35
|
end
|
39
|
-
|
40
|
-
end
|
36
|
+
end
|
data/lib/splitclient-rb.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
require 'splitclient-rb/version'
|
2
|
-
|
3
|
-
require 'splitclient-rb/split_factory_builder'
|
4
|
-
require 'splitclient-rb/localhost_split_factory'
|
5
|
-
require 'splitclient-rb/split_config'
|
2
|
+
|
6
3
|
require 'exceptions/sdk_blocker_timeout_expired_exception'
|
7
4
|
require 'cache/adapters/memory_adapters/map_adapter'
|
8
5
|
require 'cache/adapters/memory_adapters/queue_adapter'
|
@@ -20,11 +17,24 @@ require 'cache/repositories/metrics/memory_repository'
|
|
20
17
|
require 'cache/repositories/metrics/redis_repository'
|
21
18
|
require 'cache/senders/impressions_formatter'
|
22
19
|
require 'cache/senders/impressions_sender'
|
20
|
+
require 'cache/senders/metrics_sender'
|
23
21
|
require 'cache/stores/sdk_blocker'
|
24
22
|
require 'cache/stores/segment_store'
|
25
23
|
require 'cache/stores/split_store'
|
24
|
+
|
25
|
+
require 'splitclient-rb/localhost_utils'
|
26
|
+
require 'splitclient-rb/clients/localhost_split_client'
|
27
|
+
require 'splitclient-rb/clients/split_client'
|
28
|
+
require 'splitclient-rb/managers/localhost_split_manager'
|
29
|
+
require 'splitclient-rb/managers/split_manager'
|
30
|
+
require 'splitclient-rb/split_factory'
|
31
|
+
require 'splitclient-rb/split_factory_builder'
|
32
|
+
require 'splitclient-rb/localhost_split_factory'
|
33
|
+
require 'splitclient-rb/split_config'
|
34
|
+
|
26
35
|
require 'engine/api/client'
|
27
36
|
require 'engine/api/impressions'
|
37
|
+
require 'engine/api/metrics'
|
28
38
|
require 'engine/api/segments'
|
29
39
|
require 'engine/api/splits'
|
30
40
|
require 'engine/parser/condition'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
class LocalhostSplitClient
|
3
|
+
include SplitIoClient::LocalhostUtils
|
4
|
+
|
5
|
+
#
|
6
|
+
# variables to if the sdk is being used in localhost mode and store the list of features
|
7
|
+
attr_reader :localhost_mode
|
8
|
+
attr_reader :localhost_mode_features
|
9
|
+
|
10
|
+
#
|
11
|
+
# Creates a new split client instance that reads from the given splits file
|
12
|
+
#
|
13
|
+
# @param splits_file [File] file that contains some splits
|
14
|
+
#
|
15
|
+
# @return [LocalhostSplitIoClient] split.io localhost client instance
|
16
|
+
def initialize(splits_file, reload_rate = nil)
|
17
|
+
@localhost_mode = true
|
18
|
+
@localhost_mode_features = []
|
19
|
+
load_localhost_mode_features(splits_file, reload_rate)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# method that returns the sdk gem version
|
24
|
+
#
|
25
|
+
# @return [string] version value for this sdk
|
26
|
+
def self.sdk_version
|
27
|
+
'RubyClientSDK-'+SplitIoClient::VERSION
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_treatments(key, split_names, attributes = nil)
|
31
|
+
split_names.each_with_object({}) do |name, memo|
|
32
|
+
puts "name #{name} memo #{memo}"
|
33
|
+
memo.merge!(name => get_treatment(key, name, attributes))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# obtains the treatment for a given feature
|
39
|
+
#
|
40
|
+
# @param id [string] user id
|
41
|
+
# @param feature [string] name of the feature that is being validated
|
42
|
+
#
|
43
|
+
# @return [Treatment] treatment constant value
|
44
|
+
def get_treatment(id, feature, attributes = nil)
|
45
|
+
unless id
|
46
|
+
@config.logger.warn('id was null for feature: ' + feature)
|
47
|
+
return Treatments::CONTROL
|
48
|
+
end
|
49
|
+
|
50
|
+
unless feature
|
51
|
+
@config.logger.warn('feature was null for id: ' + id)
|
52
|
+
return Treatments::CONTROL
|
53
|
+
end
|
54
|
+
|
55
|
+
result = get_localhost_treatment(feature)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
#
|
61
|
+
# auxiliary method to get the treatments avoding exceptions
|
62
|
+
#
|
63
|
+
# @param id [string] user id
|
64
|
+
# @param feature [string] name of the feature that is being validated
|
65
|
+
#
|
66
|
+
# @return [Treatment] tretment constant value
|
67
|
+
def get_treatment_without_exception_handling(id, feature, attributes = nil)
|
68
|
+
get_treatment(id, feature, attributes)
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# method to check if the sdk is running in localhost mode based on api key
|
73
|
+
#
|
74
|
+
# @return [boolean] True if is in localhost mode, false otherwise
|
75
|
+
def is_localhost_mode?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# method to check the treatment for the given feature in localhost mode
|
81
|
+
#
|
82
|
+
# @return [boolean] true if the feature is available in localhost mode, false otherwise
|
83
|
+
def get_localhost_treatment(feature)
|
84
|
+
treatment = @localhost_mode_features.select { |h| h[:feature] == feature }.last || {}
|
85
|
+
|
86
|
+
treatment[:treatment] || Treatments::CONTROL
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|