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.
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