splitclient-rb 3.1.2 → 3.1.3.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +7 -0
  3. data/Detailed-README.md +20 -5
  4. data/exe/splitio +54 -3
  5. data/lib/cache/adapters/memory_adapters/map_adapter.rb +6 -0
  6. data/lib/cache/adapters/redis_adapter.rb +6 -1
  7. data/lib/cache/repositories/impressions/memory_repository.rb +8 -2
  8. data/lib/cache/repositories/impressions/redis_repository.rb +2 -2
  9. data/lib/cache/repositories/impressions_repository.rb +3 -0
  10. data/lib/cache/repositories/metrics_repository.rb +1 -0
  11. data/lib/cache/repositories/repository.rb +1 -1
  12. data/lib/cache/repositories/segments_repository.rb +16 -1
  13. data/lib/cache/repositories/splits_repository.rb +21 -7
  14. data/lib/cache/senders/impressions_sender.rb +16 -12
  15. data/lib/cache/senders/metrics_sender.rb +41 -0
  16. data/lib/cache/stores/sdk_blocker.rb +9 -8
  17. data/lib/cache/stores/segment_store.rb +1 -1
  18. data/lib/engine/api/client.rb +15 -2
  19. data/lib/engine/api/metrics.rb +57 -0
  20. data/lib/engine/parser/split_adapter.rb +9 -140
  21. data/lib/engine/parser/split_treatment.rb +7 -5
  22. data/lib/engine/partitions/treatments.rb +1 -5
  23. data/lib/splitclient-rb.rb +14 -4
  24. data/lib/splitclient-rb/clients/localhost_split_client.rb +89 -0
  25. data/lib/splitclient-rb/clients/split_client.rb +114 -0
  26. data/lib/splitclient-rb/localhost_split_factory.rb +6 -187
  27. data/lib/splitclient-rb/localhost_utils.rb +36 -0
  28. data/lib/splitclient-rb/managers/localhost_split_manager.rb +45 -0
  29. data/lib/splitclient-rb/managers/split_manager.rb +77 -0
  30. data/lib/splitclient-rb/split_config.rb +20 -0
  31. data/lib/splitclient-rb/split_factory.rb +16 -217
  32. data/lib/splitclient-rb/split_factory_builder.rb +3 -2
  33. data/lib/splitclient-rb/version.rb +1 -1
  34. data/lib/splitclient-rb_utilitites.rb +24 -19
  35. data/splitclient-rb.gemspec +1 -1
  36. data/{splitio.yml → splitio.yml.example} +0 -0
  37. 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.splits_ready
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
@@ -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::SplitFactory.sdk_version,
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
- # TODO: Send metrics in main thread for test ENV
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
- condition.matchers.each do |matcher|
40
- matchers << condition.send(
41
- "matcher_#{matcher[:matcherType].downcase}",
42
- matcher: matcher, segments_repository: @segments_repository
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
@@ -1,8 +1,5 @@
1
1
  require 'splitclient-rb/version'
2
- require 'splitclient-rb/split_factory'
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