splitclient-rb 5.1.3.pre.rc4-java → 5.1.4.pre.rc1-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dde1928ad4eddf2d5e0d485476c318ca4944cb5b
4
- data.tar.gz: 753fa3a304dfed18da1820375f4071e1f7783efc
3
+ metadata.gz: 0a45f8a3640a0089cccadfe8cc6bbeb3b21f7d57
4
+ data.tar.gz: dfcfabfd547fc59bdd4884a23133f89721f2a605
5
5
  SHA512:
6
- metadata.gz: '08abb04c5d063ce28654f665b302a36acce2a2f265c8ccbd1402a9885163a77d2e2c690576b6ef4ca5a891fdf3e5f46586abe3b74d84bed1a0839d4de23bdeba'
7
- data.tar.gz: 6f2fb60a7fb1c6609090b8cea1857ab29643f4d1ca3b27f9beac7f3a36ac14e6514eaac2429475ca8310b6d818a74fc23fc76a78afc0a4287bf7e9b58a1421d3
6
+ metadata.gz: f7fe35021e61db088ef74c6bb0b6ab8da7deff8e6a88b22c265c3ff2dd8cbbbd9db21871e70dd3cdc4abfa35743503a6d806789f9e3f475a8de7e7b612fdd28e
7
+ data.tar.gz: 594ee2a4ffa7cbe7f4d0bbad4fedc4a60e9c033d749c0ca1c019c6434de95dd2873969560ad92d15dd2f738e36e087847017f3b8a515f5adf43b4d3e77416a85
data/.rubocop.yml CHANGED
@@ -1,6 +1,3 @@
1
- Documentation:
2
- Enabled: false
3
-
4
1
  Metrics/MethodLength:
5
2
  Max: 15
6
3
 
@@ -11,6 +8,7 @@ Metrics/BlockLength:
11
8
  Exclude:
12
9
  - spec/**/*
13
10
  - splitclient-rb.gemspec
11
+ - exe/splitio
14
12
 
15
13
  Naming/FileName:
16
14
  Exclude:
data/Detailed-README.md CHANGED
@@ -72,17 +72,6 @@ else
72
72
  end
73
73
  ```
74
74
 
75
- **Note**: You can ensure the SDK resources are loaded before querying for treatments by using `block_until_ready`. See [Advanced Configuration](#advanced-configuration).
76
- ``` ruby
77
- options = {
78
- block_until_ready: 10
79
- }
80
-
81
- # Then init the factory passing the options hash
82
- factory = SplitIoClient::SplitFactoryBuilder.build('YOUR_API_KEY', options)
83
- split_client = factory.client
84
- ```
85
-
86
75
  For features that use targeting rules based on user attributes, you can call the `get_treatment` method the following way:
87
76
 
88
77
  ```ruby
@@ -250,7 +239,7 @@ The following values can be customized:
250
239
 
251
240
  *default value* = `60`
252
241
 
253
- **impressions_queue_size** : The size of the impressions queue in case of `cache_adapter == :memory`. When the queue is full, existing impressions will be dropped.
242
+ **impressions_queue_size** : The size of the impressions queue in case of `cache_adapter == :memory`.
254
243
 
255
244
  *default value* = 5000
256
245
 
@@ -258,10 +247,6 @@ The following values can be customized:
258
247
 
259
248
  *default value* = defaults to `impressions_queue_size`
260
249
 
261
- **events_queue_size** : The size of the events queue in case of `cache_adapter == :memory`. When the queue is full, existing events will be dropped.
262
-
263
- *default value* = 500
264
-
265
250
  **debug_enabled** : Enables extra logging (verbose mode).
266
251
 
267
252
  *default value* = `false`
@@ -344,9 +329,15 @@ options = {
344
329
 
345
330
  ```ruby
346
331
  options = {
347
- # other options
332
+ connection_timeout: 10,
333
+ read_timeout: 5,
334
+ features_refresh_rate: 120,
335
+ segments_refresh_rate: 120,
336
+ metrics_refresh_rate: 360,
337
+ impressions_refresh_rate: 360,
338
+ logger: Logger.new('logfile.log'),
348
339
  cache_adapter: :redis,
349
- mode: :consumer,
340
+ mode: :standalone,
350
341
  redis_url: 'redis://127.0.0.1:6379/0'
351
342
  }
352
343
  begin
@@ -397,10 +388,10 @@ In the example above, the listener simply takes an impression and logs it to the
397
388
 
398
389
  The SDK is capable of running in two different modes to fit in different infrastructure configurations:
399
390
 
400
- - `:standalone` - (default) : The SDK will retrieve information (e.g. split definitions) periodically from the Split servers, and store it in the memory cache. It'll also store the application execution information (e.g. impressions) in the cache and send it periodically to the Split servers. As it name implies, in this mode, the SDK neither relies nor synchronizes with any other component. _This mode is only available when using the `memory` cache adapter._
401
- - `:consumer` - If using a load balancer or more than one SDK in your application, guaranteeing that all changes in split definitions are picked up by all SDK instances at the same time is highly recommended in order to ensure consistent results across your infrastructure (i.e. getting the same treatment for a specific split and user pair). To achieve this, use the [Split Synchronizer](https://docs.split.io/docs/split-synchronizer)) and setup your SDKs to work in the `consumer` mode. Setting the components this way, all communication with the Split server is orchestrated by the Synchronizer, while the SDKs pick up definitions and store the execution information from / into a shared Redis data store. _This mode is only available when using the `redis` cache adapter._
391
+ - `:standalone` - (default) : The SDK will retrieve information (e.g. split definitions) periodically from the Split servers, and store it in the chosen cache (memory / Redis). It'll also store the application execution information (e.g. impressions) in the cache and send it periodically to the Split servers. As it name implies, in this mode, the SDK neither relies nor synchronizes with any other component.
392
+ - `:consumer` - If using a load balancer or more than one SDK in your application, guaranteeing that all changes in split definitions are picked up by all SDK instances at the same time is highly recommended in order to ensure consistent results across your infrastructure (i.e. getting the same treatment for a specific split and user pair). To achieve this, use the [Split Synchronizer](https://docs.split.io/docs/split-synchronizer)) and setup your SDKs to work in the `consumer` mode. Setting the components this way, all communication with the Split server is orchestrated by the Synchronizer, while the SDKs pick up definitions and store the execution information from / into a shared Redis data store.
402
393
 
403
- _You can choose between these 2 modes setting the `mode` option in the config. An invalid combination of `cache_adaper` and `mode` will result in an Invalid Mode exception being raised._
394
+ _You can choose between these 2 modes setting the `mode` option in the config._
404
395
 
405
396
  ## SDK Server Compatibility
406
397
 
data/exe/splitio ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ require 'optparse'
8
+ require 'yaml'
9
+ require_relative '../lib/splitclient-rb'
10
+
11
+ # ARGV << '-h' if ARGV.empty?
12
+
13
+ config_path = ''
14
+ options = {}
15
+ opt_parser = OptionParser.new do |opts|
16
+ opts.banner = 'Usage: splitio [options]'
17
+
18
+ opts.on('-cPATH', '--config=PATH', 'Set the path to splitio.yml config file') do |c|
19
+ config_path = c
20
+ end
21
+
22
+ opts.on('--base-uri=BASE_URI', 'Set the base uri for Split SDK') do |c|
23
+ options[:base_uri] = c
24
+ end
25
+
26
+ opts.on('--events-uri=EVENTS_URI', 'Set the events uri for Split SDK') do |c|
27
+ options[:events_uri] = c
28
+ end
29
+
30
+ opts.on('--api-key=API_KEY', 'Set the API Key for Split SDK') do |c|
31
+ options[:api_key] = c
32
+ end
33
+
34
+ opts.on('--read-timeout=READ_TIMEOUT', 'Read timeout in seconds') do |c|
35
+ options[:read_timeout] = c
36
+ end
37
+
38
+ opts.on('--connection-timeout=CONNECTION_TIMEOUT', 'Connection timeout in seconds') do |c|
39
+ options[:connection_timeout] = c
40
+ end
41
+
42
+ opts.on('--features-refresh-rate=FEATURES_REFRESH_RATE', 'Features refresh rate in seconds') do |c|
43
+ options[:features_refresh_rate] = c
44
+ end
45
+
46
+ opts.on('--segments-refresh-rate=SEGMENTS_REFRESH_RATE', 'Segments refresh rate in seconds') do |c|
47
+ options[:segments_refresh_rate] = c
48
+ end
49
+
50
+ opts.on('--metrics-refresh-rate=METRICS_REFRESH_RATE', 'Metrics refresh rate in seconds') do |c|
51
+ options[:metrics_refresh_rate] = c
52
+ end
53
+
54
+ opts.on('--impressions-refresh-rate=IMPRESSIONS_REFRESH_RATE', 'Impressions refresh rate in seconds') do |c|
55
+ options[:impressions_refresh_rate] = c
56
+ end
57
+
58
+ opts.on('--ready=SECONDS', 'Seconds to block the app until SDK is ready or false to run in non-blocking mode') do |c|
59
+ options[:ready] = c
60
+ end
61
+
62
+ opts.on('--redis-url=REDIS_URL', 'Set base uri for Split SDK') do |c|
63
+ options[:redis_url] = c
64
+ end
65
+
66
+ opts.on('--transport-debug', 'Enable transport debug') do
67
+ options[:transport_debug_enabled] = true
68
+ end
69
+
70
+ opts.on('-d', '--debug', 'Enable debug mode') do
71
+ options[:debug_enabled] = true
72
+ end
73
+
74
+ opts.on_tail('-h', '--help', 'Prints this help') do
75
+ puts opts
76
+ exit
77
+ end
78
+ end
79
+
80
+ begin
81
+ opt_parser.parse!(ARGV)
82
+ rescue OptionParser::InvalidOption => e
83
+ puts e
84
+ puts opt_parser
85
+ exit(1)
86
+ end
87
+
88
+ config = config_path != '' ? YAML.load_file(config_path) : {}
89
+ config
90
+ .merge!(mode: :producer, cache_adapter: :redis)
91
+ .merge!(options)
92
+ .merge!(api_key: ENV['API_KEY'] || config[:api_key])
93
+ .merge!(base_uri: ENV['SDK_URI'] || config[:base_uri])[:events_uri] = ENV['EVENTS_URI'] || config[:events_uri]
94
+ # IDENTIFY_BASE_URI
95
+
96
+ SplitIoClient::SplitFactory.new(config[:api_key], config)
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'json'
4
2
 
5
3
  module SplitIoClient
@@ -87,8 +85,8 @@ module SplitIoClient
87
85
  end
88
86
 
89
87
  # Set
90
- alias initialize_set initialize_map
91
- alias find_sets_by_prefix find_strings_by_prefix
88
+ alias_method :initialize_set, :initialize_map
89
+ alias_method :find_sets_by_prefix, :find_strings_by_prefix
92
90
 
93
91
  def add_to_set(key, val)
94
92
  @redis.sadd(key, val)
@@ -128,7 +126,7 @@ module SplitIoClient
128
126
  def get_from_queue(key, count)
129
127
  items = @redis.lrange(key, 0, count - 1)
130
128
  fetched_count = items.size
131
- items_to_remove = fetched_count == count ? count : fetched_count
129
+ items_to_remove = (fetched_count == count) ? count : fetched_count
132
130
 
133
131
  @redis.ltrim(key, items_to_remove, -1)
134
132
 
@@ -150,9 +148,9 @@ module SplitIoClient
150
148
  @redis.incrby(key, inc)
151
149
  end
152
150
 
153
- def pipelined
151
+ def pipelined(&block)
154
152
  @redis.pipelined do
155
- yield
153
+ block.call
156
154
  end
157
155
  end
158
156
 
@@ -161,10 +159,6 @@ module SplitIoClient
161
159
 
162
160
  keys.map { |key| @redis.del(key) }
163
161
  end
164
-
165
- def expire(key, seconds)
166
- @redis.expire(key, seconds)
167
- end
168
162
  end
169
163
  end
170
164
  end
@@ -1,44 +1,42 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Repositories
6
4
  module Impressions
7
- class MemoryRepository < ImpressionsRepository
5
+ class MemoryRepository
6
+
8
7
  def initialize(adapter)
9
8
  @adapter = adapter
10
9
  end
11
10
 
12
11
  # Store impression data in the selected adapter
13
- def add(matching_key, bucketing_key, split_name, treatment, time)
14
- @adapter.add_to_queue(
15
- m: metadata,
16
- i: impression_data(
17
- matching_key,
18
- bucketing_key,
19
- split_name,
20
- treatment,
21
- time
22
- )
23
- )
12
+ def add(split_name, data)
13
+ @adapter.add_to_queue(feature: split_name, impressions: data)
24
14
  rescue ThreadError # queue is full
25
15
  if random_sampler.rand(1..1000) <= 2 # log only 0.2 % of the time
26
- SplitIoClient.configuration.logger.warn("Dropping impressions. Current size is \
27
- #{SplitIoClient.configuration.impressions_queue_size}. " \
28
- 'Consider increasing impressions_queue_size')
16
+ SplitIoClient.configuration.logger.warn("Dropping impressions. Current size is #{SplitIoClient.configuration.impressions_queue_size}. " \
17
+ "Consider increasing impressions_queue_size")
29
18
  end
30
19
  end
31
20
 
32
21
  def add_bulk(key, bucketing_key, treatments, time)
33
22
  treatments.each do |split_name, treatment|
34
- add(key, bucketing_key, split_name, treatment, time)
23
+ add(
24
+ split_name,
25
+ 'keyName' => key,
26
+ 'bucketingKey' => bucketing_key,
27
+ 'treatment' => treatment[:treatment],
28
+ 'label' => SplitIoClient.configuration.labels_enabled ? treatment[:label] : nil,
29
+ 'changeNumber' => treatment[:change_number],
30
+ 'time' => time
31
+ )
35
32
  end
36
33
  end
37
34
 
38
- def batch
39
- return [] if SplitIoClient.configuration.impressions_bulk_size.zero?
40
-
41
- @adapter.get_batch(SplitIoClient.configuration.impressions_bulk_size)
35
+ def get_batch
36
+ return [] if SplitIoClient.configuration.impressions_bulk_size == 0
37
+ @adapter.get_batch(SplitIoClient.configuration.impressions_bulk_size).map do |impression|
38
+ impression.update(ip: SplitIoClient.configuration.machine_ip)
39
+ end
42
40
  end
43
41
 
44
42
  private
@@ -1,53 +1,75 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Repositories
6
4
  module Impressions
7
- class RedisRepository < ImpressionsRepository
8
- EXPIRE_SECONDS = 3600
5
+ class RedisRepository < Repository
9
6
 
10
7
  def initialize(adapter)
11
8
  @adapter = adapter
12
9
  end
13
10
 
14
- def add(matching_key, bucketing_key, split_name, treatment, time)
15
- add_bulk(matching_key, bucketing_key, { split_name => treatment }, time)
11
+ # Store impression data in Redis
12
+ def add(split_name, data)
13
+ @adapter.add_to_set(
14
+ impressions_metrics_key("impressions.#{split_name}"),
15
+ data.to_json
16
+ )
16
17
  end
17
18
 
18
- def add_bulk(matching_key, bucketing_key, treatments, time)
19
- impressions = treatments.map do |split_name, treatment|
20
- {
21
- m: metadata,
22
- i: impression_data(
23
- matching_key,
24
- bucketing_key,
25
- split_name,
26
- treatment,
27
- time
28
- )
29
- }.to_json
19
+ def add_bulk(key, bucketing_key, treatments, time)
20
+ @adapter.redis.pipelined do
21
+ treatments.each do |split_name, treatment|
22
+ add(split_name,
23
+ 'keyName' => key,
24
+ 'bucketingKey' => bucketing_key,
25
+ 'treatment' => treatment[:treatment],
26
+ 'label' => SplitIoClient.configuration.labels_enabled ? treatment[:label] : nil,
27
+ 'changeNumber' => treatment[:change_number],
28
+ 'time' => time)
29
+ end
30
30
  end
31
+ end
31
32
 
32
- impressions_list_size = @adapter.add_to_queue(key, impressions)
33
+ # Get random impressions from redis in batches of size SplitIoClient.configuration.impressions_bulk_size,
34
+ # delete fetched impressions afterwards
35
+ def get_batch
36
+ impressions = impression_keys.each_with_object([]) do |key, memo|
37
+ ip = key.split('/')[-2] # 'prefix/sdk_lang/ip/impressions.name' -> ip
38
+ if ip.nil?
39
+ SplitIoClient.configuration.logger.warn("Impressions IP parse error for key: #{key}")
40
+ next
41
+ end
42
+ split_name = key.split('.').last
43
+ members = @adapter.random_set_elements(key, SplitIoClient.configuration.impressions_bulk_size)
44
+ members.each do |impression|
45
+ parsed_impression = JSON.parse(impression)
33
46
 
34
- # Synchronizer might not be running
35
- @adapter.expire(key, EXPIRE_SECONDS) if impressions.size == impressions_list_size
36
- end
47
+ memo << {
48
+ feature: split_name.to_sym,
49
+ impressions: parsed_impression,
50
+ ip: ip
51
+ }
52
+ end
53
+
54
+ @adapter.delete_from_set(key, members)
37
55
 
38
- def batch
39
- @adapter.get_from_queue(key, SplitIoClient.configuration.impressions_bulk_size).map do |e|
40
- impression = JSON.parse(e, symbolize_names: true)
41
- impression[:i][:f] = impression[:i][:f].to_sym
42
- impression
43
56
  end
57
+ impressions
44
58
  rescue StandardError => e
45
59
  SplitIoClient.configuration.logger.error("Exception while clearing impressions cache: #{e}")
60
+
46
61
  []
47
62
  end
48
63
 
49
- def key
50
- @key ||= namespace_key('.impressions')
64
+ private
65
+
66
+ # Get all sets by prefix
67
+ def impression_keys
68
+ @adapter.find_sets_by_prefix("#{SplitIoClient.configuration.redis_namespace}/*/impressions.*")
69
+ rescue StandardError => e
70
+ SplitIoClient.configuration.logger.error("Exception while fetching impression_keys: #{e}")
71
+
72
+ []
51
73
  end
52
74
  end
53
75
  end
@@ -1,46 +1,18 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Repositories
6
4
  # Repository which forwards impressions interface to the selected adapter
7
5
  class ImpressionsRepository < Repository
8
6
  extend Forwardable
9
- def_delegators :@adapter, :add, :add_bulk, :batch, :empty?
7
+ def_delegators :@adapter, :add, :add_bulk, :get_batch, :empty?
10
8
 
11
9
  def initialize(adapter)
12
10
  @adapter = case adapter.class.to_s
13
- when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
14
- Repositories::Impressions::MemoryRepository.new(adapter)
15
- when 'SplitIoClient::Cache::Adapters::RedisAdapter'
16
- Repositories::Impressions::RedisRepository.new(adapter)
17
- end
18
- end
19
-
20
- protected
21
-
22
- def impression_data(matching_key, bucketing_key, split_name, treatment, timestamp)
23
- {
24
- k: matching_key,
25
- b: bucketing_key,
26
- f: split_name,
27
- t: treatment[:treatment],
28
- r: applied_rule(treatment[:label]),
29
- c: treatment[:change_number],
30
- m: timestamp
31
- }
32
- end
33
-
34
- def metadata
35
- {
36
- s: "#{SplitIoClient.configuration.language}-#{SplitIoClient.configuration.version}",
37
- i: SplitIoClient.configuration.machine_ip,
38
- n: SplitIoClient.configuration.machine_name
39
- }
40
- end
41
-
42
- def applied_rule(label)
43
- SplitIoClient.configuration.labels_enabled ? label : nil
11
+ when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
12
+ Repositories::Impressions::MemoryRepository.new(adapter)
13
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
14
+ Repositories::Impressions::RedisRepository.new(adapter)
15
+ end
44
16
  end
45
17
  end
46
18
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Senders
@@ -38,14 +36,10 @@ module SplitIoClient
38
36
  end
39
37
 
40
38
  def post_events
41
- events_api.post(@events_repository.clear)
39
+ SplitIoClient::Api::Events.new(@api_key, @events_repository.clear).post
42
40
  rescue StandardError => error
43
41
  SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
44
42
  end
45
-
46
- def events_api
47
- @events_api ||= SplitIoClient::Api::Events.new(@api_key)
48
- end
49
43
  end
50
44
  end
51
45
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Senders
@@ -9,15 +7,29 @@ module SplitIoClient
9
7
  end
10
8
 
11
9
  def call(raw_impressions)
12
- impressions = raw_impressions || @impressions_repository.batch
10
+ impressions = raw_impressions ? raw_impressions : @impressions_repository.get_batch
11
+ formatted_impressions = []
13
12
  filtered_impressions = filter_impressions(impressions)
14
13
 
15
14
  return [] if impressions.empty? || filtered_impressions.empty?
16
15
 
17
16
  formatted_impressions = unique_features(filtered_impressions).each_with_object([]) do |feature, memo|
18
- feature_impressions = feature_impressions(filtered_impressions, feature)
19
- ip = feature_impressions.first[:m][:i]
20
- current_impressions = current_impressions(feature_impressions)
17
+ ip = nil
18
+ current_impressions =
19
+ filtered_impressions
20
+ .select { |impression| impression[:feature] == feature }
21
+ .map do |impression|
22
+ ip = impression[:ip]
23
+ {
24
+ keyName: impression[:impressions]['keyName'] || impression[:impressions]['key_name'],
25
+ treatment: impression[:impressions]['treatment'],
26
+ time: impression[:impressions]['time'],
27
+ bucketingKey: impression[:impressions]['bucketingKey'] || impression[:impressions]['bucketing_key'],
28
+ label: impression[:impressions]['label'],
29
+ changeNumber: impression[:impressions]['changeNumber'] || impression[:impressions]['change_number'],
30
+ }
31
+ end
32
+
21
33
  memo << {
22
34
  testName: feature.to_sym,
23
35
  keyImpressions: current_impressions,
@@ -30,27 +42,8 @@ module SplitIoClient
30
42
 
31
43
  private
32
44
 
33
- def feature_impressions(filtered_impressions, feature)
34
- filtered_impressions.select do |impression|
35
- impression[:i][:f] == feature
36
- end
37
- end
38
-
39
- def current_impressions(feature_impressions)
40
- feature_impressions.map do |impression|
41
- {
42
- keyName: impression[:i][:k],
43
- treatment: impression[:i][:t],
44
- time: impression[:i][:m],
45
- bucketingKey: impression[:i][:b],
46
- label: impression[:i][:r],
47
- changeNumber: impression[:i][:c]
48
- }
49
- end
50
- end
51
-
52
45
  def unique_features(impressions)
53
- impressions.map { |impression| impression[:i][:f] }.uniq
46
+ impressions.map { |impression| impression[:feature] }.uniq
54
47
  end
55
48
 
56
49
  # Filter seen impressions by impression_hash
@@ -68,11 +61,11 @@ module SplitIoClient
68
61
  end
69
62
 
70
63
  def impression_hash(impression)
71
- "#{impression[:i][:f]}:" \
72
- "#{impression[:i][:k]}:" \
73
- "#{impression[:i][:b]}:" \
74
- "#{impression[:i][:c]}:" \
75
- "#{impression[:i][:t]}"
64
+ "#{impression[:feature]}:" \
65
+ "#{impression[:impressions]['keyName']}:" \
66
+ "#{impression[:impressions]['bucketingKey']}:" \
67
+ "#{impression[:impressions]['changeNumber']}:" \
68
+ "#{impression[:impressions]['treatment']}"
76
69
  end
77
70
  end
78
71
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Senders
@@ -49,7 +47,7 @@ module SplitIoClient
49
47
  end
50
48
 
51
49
  def post_impressions
52
- impressions_api.post(formatted_impressions)
50
+ impressions_client.post
53
51
  rescue StandardError => error
54
52
  SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
55
53
  end
@@ -58,8 +56,8 @@ module SplitIoClient
58
56
  ImpressionsFormatter.new(@impressions_repository).call(raw_impressions)
59
57
  end
60
58
 
61
- def impressions_api
62
- @impressions_api ||= SplitIoClient::Api::Impressions.new(@api_key)
59
+ def impressions_client
60
+ SplitIoClient::Api::Impressions.new(@api_key, formatted_impressions)
63
61
  end
64
62
  end
65
63
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Senders
@@ -36,13 +34,13 @@ module SplitIoClient
36
34
  end
37
35
 
38
36
  def post_metrics
39
- metrics_api.post
37
+ metrics_client.post
40
38
  rescue StandardError => error
41
39
  SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
42
40
  end
43
41
 
44
- def metrics_api
45
- @metrics_api ||= SplitIoClient::Api::Metrics.new(@api_key, @metrics_repository)
42
+ def metrics_client
43
+ SplitIoClient::Api::Metrics.new(@api_key, @metrics_repository)
46
44
  end
47
45
  end
48
46
  end
@@ -73,7 +73,7 @@ module SplitIoClient
73
73
  end
74
74
 
75
75
  def segments_api
76
- @segments_api ||= SplitIoClient::Api::Segments.new(@api_key, @metrics, @segments_repository)
76
+ SplitIoClient::Api::Segments.new(@api_key, @metrics, @segments_repository)
77
77
  end
78
78
  end
79
79
  end
@@ -66,7 +66,7 @@ module SplitIoClient
66
66
  end
67
67
 
68
68
  def splits_since(since)
69
- splits_api.since(since)
69
+ SplitIoClient::Api::Splits.new(@api_key, @metrics).since(since)
70
70
  end
71
71
 
72
72
  def add_split_unless_archived(split)
@@ -90,12 +90,6 @@ module SplitIoClient
90
90
 
91
91
  @splits_repository.add_split(split)
92
92
  end
93
-
94
- private
95
-
96
- def splits_api
97
- @splits_api ||= SplitIoClient::Api::Splits.new(@api_key, @metrics)
98
- end
99
93
  end
100
94
  end
101
95
  end
@@ -136,12 +136,13 @@ module SplitIoClient
136
136
 
137
137
  return if SplitIoClient.configuration.disable_impressions || !store_impressions
138
138
 
139
- @impressions_repository.add(
140
- matching_key,
141
- bucketing_key,
142
- split_name,
143
- treatment,
144
- time
139
+ @impressions_repository.add(split_name,
140
+ 'keyName' => matching_key,
141
+ 'bucketingKey' => bucketing_key,
142
+ 'treatment' => treatment[:treatment],
143
+ 'label' => SplitIoClient.configuration.labels_enabled ? treatment[:label] : nil,
144
+ 'time' => time,
145
+ 'changeNumber' => treatment[:change_number]
145
146
  )
146
147
 
147
148
  route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes)
@@ -38,7 +38,8 @@ module SplitIoClient
38
38
  end
39
39
  rescue StandardError => e
40
40
  SplitIoClient.configuration.logger.warn("#{e}\nURL:#{url}\ndata:#{data}\nparams:#{params}")
41
- raise 'Split SDK failed to connect to backend to retrieve information'
41
+
42
+ false
42
43
  end
43
44
 
44
45
  private
@@ -1,32 +1,31 @@
1
1
  module SplitIoClient
2
2
  module Api
3
3
  class Events < Client
4
- def initialize(api_key)
4
+ def initialize(api_key, events)
5
5
  @api_key = api_key
6
+ @events = events
6
7
  end
7
8
 
8
- def post(events)
9
- if events.empty?
9
+ def post
10
+ if @events.empty?
10
11
  SplitIoClient.configuration.logger.debug('No events to report') if SplitIoClient.configuration.debug_enabled
11
12
  return
12
13
  end
13
14
 
14
- events.each_slice(SplitIoClient.configuration.events_queue_size) do |events_slice|
15
+ @events.each_slice(SplitIoClient.configuration.events_queue_size) do |event_slice|
15
16
  result = post_api(
16
17
  "#{SplitIoClient.configuration.events_uri}/events/bulk",
17
18
  @api_key,
18
- events_slice.map { |event| formatted_event(event[:e]) },
19
- 'SplitSDKMachineIP' => events_slice[0][:m][:i],
20
- 'SplitSDKMachineName' => events_slice[0][:m][:n],
21
- 'SplitSDKVersion' => events_slice[0][:m][:s]
19
+ event_slice.map { |event| formatted_event(event[:e]) },
20
+ 'SplitSDKMachineIP' => event_slice[0][:m][:i],
21
+ 'SplitSDKMachineName' => event_slice[0][:m][:n],
22
+ 'SplitSDKVersion' => event_slice[0][:m][:s]
22
23
  )
23
24
 
24
- if response.success?
25
- SplitLogger.log_if_debug("Events reported: #{events_slice.size}")
25
+ if (200..299).include? result.status
26
+ SplitIoClient.configuration.logger.debug("Events reported: #{event_slice.size}") if SplitIoClient.configuration.debug_enabled
26
27
  else
27
- SplitLogger.log_error("Unexpected status code while posting events: #{response.status}." \
28
- " - Check your API key and base URI")
29
- raise 'Split SDK failed to connect to backend to post events'
28
+ SplitIoClient.configuration.logger.error("Unexpected status code while posting events: #{result.status}")
30
29
  end
31
30
  end
32
31
  end
@@ -1,25 +1,24 @@
1
1
  module SplitIoClient
2
2
  module Api
3
3
  class Impressions < Client
4
- def initialize(api_key)
4
+ def initialize(api_key, impressions)
5
5
  @api_key = api_key
6
+ @impressions = impressions
6
7
  end
7
8
 
8
- def post(impressions)
9
- if impressions.empty?
9
+ def post
10
+ if @impressions.empty?
10
11
  SplitIoClient.configuration.logger.debug('No impressions to report') if SplitIoClient.configuration.debug_enabled
11
12
  return
12
13
  end
13
14
 
14
- impressions_by_ip(impressions).each do |ip, impressions_ip|
15
- result = post_api("#{SplitIoClient.configuration.events_uri}/testImpressions/bulk", @api_key, impressions_ip, 'SplitSDKMachineIP' => ip)
15
+ impressions_by_ip.each do |ip, impressions|
16
+ result = post_api("#{SplitIoClient.configuration.events_uri}/testImpressions/bulk", @api_key, impressions, 'SplitSDKMachineIP' => ip)
16
17
 
17
- if response.success?
18
- SplitLogger.log_if_debug("Impressions reported: #{total_impressions(impressions)}")
18
+ if (200..299).include? result.status
19
+ SplitIoClient.configuration.logger.debug("Impressions reported: #{total_impressions(@impressions)}") if SplitIoClient.configuration.debug_enabled
19
20
  else
20
- SplitLogger.log_error("Unexpected status code while posting impressions: #{response.status}." \
21
- " - Check your API key and base URI")
22
- raise 'Split SDK failed to connect to backend to post impressions'
21
+ SplitIoClient.configuration.logger.error("Unexpected status code while posting impressions: #{result.status}")
23
22
  end
24
23
  end
25
24
  end
@@ -34,8 +33,8 @@ module SplitIoClient
34
33
 
35
34
  private
36
35
 
37
- def impressions_by_ip(impressions)
38
- impressions.group_by { |impression| impression[:ip] }
36
+ def impressions_by_ip
37
+ @impressions.group_by { |impression| impression[:ip] }
39
38
  end
40
39
  end
41
40
  end
@@ -20,9 +20,9 @@ module SplitIoClient
20
20
  @metrics_repository.latencies.each do |name, latencies|
21
21
  metrics_time = { name: name, latencies: latencies }
22
22
 
23
- response = post_api("#{SplitIoClient.configuration.events_uri}/metrics/time", @api_key, metrics_time)
23
+ result = post_api("#{SplitIoClient.configuration.events_uri}/metrics/time", @api_key, metrics_time)
24
24
 
25
- log_status(response, metrics_time.size)
25
+ log_status(result, metrics_time.size)
26
26
  end
27
27
  end
28
28
 
@@ -36,9 +36,9 @@ module SplitIoClient
36
36
  @metrics_repository.counts.each do |name, count|
37
37
  metrics_count = { name: name, delta: count }
38
38
 
39
- response = post_api("#{SplitIoClient.configuration.events_uri}/metrics/counter", @api_key, metrics_count)
39
+ result = post_api("#{SplitIoClient.configuration.events_uri}/metrics/counter", @api_key, metrics_count)
40
40
 
41
- log_status(response, metrics_count.size)
41
+ log_status(result, metrics_count.size)
42
42
  end
43
43
  end
44
44
  @metrics_repository.clear_counts
@@ -46,13 +46,13 @@ module SplitIoClient
46
46
 
47
47
  private
48
48
 
49
- def log_status(response, info_to_log)
50
- if response.success?
51
- SplitLogger.log_if_debug("Metric time reported: #{info_to_log}")
49
+ def log_status(result, info_to_log)
50
+ if result == false
51
+ SplitIoClient.configuration.logger.error("Failed to make a http request")
52
+ elsif (200..299).include? result.status
53
+ SplitIoClient.configuration.logger.debug("Metric time reported: #{info_to_log}") if SplitIoClient.configuration.debug_enabled
52
54
  else
53
- SplitLogger.log_error("Unexpected status code while posting time metrics: #{response.status}" \
54
- " - Check your API key and base URI")
55
- raise 'Split SDK failed to connect to backend to post metrics'
55
+ SplitIoClient.configuration.logger.error("Unexpected status code while posting time metrics: #{result.status}")
56
56
  end
57
57
  end
58
58
  end
@@ -51,7 +51,7 @@ module SplitIoClient
51
51
  if split[:trafficAllocation] < 100
52
52
  bucket = splitter.bucket(splitter.count_hash(key, split[:trafficAllocationSeed].to_i, legacy_algo))
53
53
 
54
- if bucket > split[:trafficAllocation]
54
+ if bucket >= split[:trafficAllocation]
55
55
  return treatment_hash(Models::Label::NOT_IN_SPLIT, split[:defaultTreatment], split[:changeNumber])
56
56
  end
57
57
  end
@@ -24,15 +24,7 @@ module SplitIoClient
24
24
  # @param sdk_blocker [SDKBlocker] SDKBlocker instance which blocks splits_repository/segments_repository
25
25
  #
26
26
  # @return [SplitIoClient] split.io client instance
27
- def initialize(
28
- api_key,
29
- splits_repository,
30
- segments_repository,
31
- impressions_repository,
32
- metrics_repository,
33
- events_repository,
34
- sdk_blocker
35
- )
27
+ def initialize(api_key, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker)
36
28
  @api_key = api_key
37
29
  @splits_repository = splits_repository
38
30
  @segments_repository = segments_repository
@@ -42,15 +34,28 @@ module SplitIoClient
42
34
  @metrics = Metrics.new(100, @metrics_repository)
43
35
  @sdk_blocker = sdk_blocker
44
36
 
45
- start_standalone_components if SplitIoClient.configuration.mode == :standalone
37
+ start_based_on_mode(SplitIoClient.configuration.mode)
46
38
  end
47
39
 
48
- def start_standalone_components
49
- split_store
50
- segment_store
51
- metrics_sender
52
- impressions_sender
53
- events_sender
40
+ def start_based_on_mode(mode)
41
+ case mode
42
+ when :standalone
43
+ split_store
44
+ segment_store
45
+ metrics_sender
46
+ impressions_sender
47
+ events_sender
48
+ when :consumer
49
+ # Do nothing in background
50
+ when :producer
51
+ split_store
52
+ segment_store
53
+ impressions_sender
54
+ metrics_sender
55
+ events_sender
56
+
57
+ sleep unless ENV['SPLITCLIENT_ENV'] == 'test'
58
+ end
54
59
  end
55
60
 
56
61
  # Starts thread which loops constantly and stores splits in the splits_repository of choice
@@ -109,7 +109,7 @@ module SplitIoClient
109
109
  #
110
110
  # The mode SDK will run
111
111
  #
112
- # @return [Symbol] One of the available SDK modes: standalone, consumer
112
+ # @return [Symbol] One of the available SDK modes: standalone, consumer, producer
113
113
  attr_accessor :mode
114
114
 
115
115
  # The read timeout for network connections in seconds.
@@ -8,9 +8,6 @@ module SplitIoClient
8
8
  def initialize(api_key, config_hash = {})
9
9
  @api_key = api_key
10
10
  SplitIoClient.configure(config_hash)
11
-
12
- raise 'Invalid SDK mode' unless valid_mode
13
-
14
11
  @cache_adapter = SplitIoClient.configuration.cache_adapter
15
12
 
16
13
  @splits_repository = SplitsRepository.new(@cache_adapter)
@@ -36,34 +33,6 @@ module SplitIoClient
36
33
  SplitIoClient.configuration.threads.each { |_, t| t.exit }
37
34
  end
38
35
 
39
- def valid_mode
40
- valid_startup_mode = false
41
- case SplitIoClient.configuration.mode
42
- when :consumer
43
- if SplitIoClient.configuration.cache_adapter.is_a? SplitIoClient::Cache::Adapters::RedisAdapter
44
- valid_startup_mode = true
45
- else
46
- SplitIoClient.configuration.logger.error('Consumer mode cannot be used with Memory adapter. ' \
47
- 'Use Redis adapter instead.')
48
- end
49
- when :standalone
50
- if SplitIoClient.configuration.cache_adapter.is_a? SplitIoClient::Cache::Adapters::MemoryAdapter
51
- valid_startup_mode = true
52
- else
53
- SplitIoClient.configuration.logger.error('Standalone mode cannot be used with Redis adapter. ' \
54
- 'Use Memory adapter instead.')
55
- end
56
- when :producer
57
- SplitIoClient.configuration.logger.error('Producer mode is no longer supported. Use Split Synchronizer. ' \
58
- 'See: https://github.com/splitio/split-synchronizer')
59
- else
60
- SplitIoClient.configuration.logger.error('Invalid SDK mode selected. ' \
61
- "Valid modes are 'standalone with memory adapter' and 'consumer with redis adapter'")
62
- end
63
-
64
- valid_startup_mode
65
- end
66
-
67
36
  alias resume! start!
68
37
  end
69
38
  end
@@ -1,14 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
1
  module SplitIoClient
4
2
  module Validators
5
3
  extend self
6
4
 
7
5
  def valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
8
- valid_key?(key) &&
9
- valid_split_name?(split_name) &&
10
- valid_matching_key?(matching_key) &&
11
- valid_bucketing_key?(key, bucketing_key)
6
+ valid_key?(key) && valid_split_name?(split_name) && valid_matching_key?(matching_key) && valid_bucketing_key?(bucketing_key)
12
7
  end
13
8
 
14
9
  def valid_get_treatments_parameters(split_names)
@@ -16,10 +11,7 @@ module SplitIoClient
16
11
  end
17
12
 
18
13
  def valid_track_parameters(key, traffic_type_name, event_type, value)
19
- valid_track_key?(key) &&
20
- valid_traffic_type_name?(traffic_type_name) &&
21
- valid_event_type?(event_type) &&
22
- valid_value?(value)
14
+ valid_track_key?(key) && valid_traffic_type_name?(traffic_type_name) && valid_event_type?(event_type) && valid_value?(value)
23
15
  end
24
16
 
25
17
  def valid_split_parameters(split_name)
@@ -52,7 +44,7 @@ module SplitIoClient
52
44
  SplitIoClient.configuration.logger.warn("#{method}: #{key} is not of type String, converting to String")
53
45
  end
54
46
 
55
- def valid_split_name?(split_name, method = :get_treatment)
47
+ def valid_split_name?(split_name, method=:get_treatment)
56
48
  if split_name.nil?
57
49
  log_nil(:split_name, method)
58
50
  return false
@@ -63,7 +55,7 @@ module SplitIoClient
63
55
  return false
64
56
  end
65
57
 
66
- true
58
+ return true
67
59
  end
68
60
 
69
61
  def valid_key?(key)
@@ -72,7 +64,7 @@ module SplitIoClient
72
64
  return false
73
65
  end
74
66
 
75
- true
67
+ return true
76
68
  end
77
69
 
78
70
  def valid_matching_key?(matching_key)
@@ -86,16 +78,16 @@ module SplitIoClient
86
78
  return false
87
79
  end
88
80
 
89
- log_convert_numeric(:matching_key, :get_treatment) if matching_key.is_a? Numeric
81
+ if matching_key.is_a? Numeric
82
+ log_convert_numeric(:matching_key, :get_treatment)
83
+ end
90
84
 
91
- true
85
+ return true
92
86
  end
93
87
 
94
- def valid_bucketing_key?(key, bucketing_key)
88
+ def valid_bucketing_key?(bucketing_key)
95
89
  if bucketing_key.nil?
96
- if key.is_a? Hash
97
- SplitIoClient.configuration.logger.warn('get_treatment: key object should have bucketing_key set')
98
- end
90
+ SplitIoClient.configuration.logger.warn('get_treatment: key object should have bucketing_key set')
99
91
  return true
100
92
  end
101
93
 
@@ -104,9 +96,11 @@ module SplitIoClient
104
96
  return false
105
97
  end
106
98
 
107
- log_convert_numeric(:bucketing_key, :get_treatment) if bucketing_key.is_a? Numeric
99
+ if bucketing_key.is_a? Numeric
100
+ log_convert_numeric(:bucketing_key, :get_treatment)
101
+ end
108
102
 
109
- true
103
+ return true
110
104
  end
111
105
 
112
106
  def valid_split_names?(split_names)
@@ -120,7 +114,7 @@ module SplitIoClient
120
114
  return false
121
115
  end
122
116
 
123
- true
117
+ return true
124
118
  end
125
119
 
126
120
  def valid_track_key?(key)
@@ -134,9 +128,11 @@ module SplitIoClient
134
128
  return false
135
129
  end
136
130
 
137
- log_convert_numeric(:key, :track) if key.is_a? Numeric
131
+ if key.is_a? Numeric
132
+ log_convert_numeric(:key, :track)
133
+ end
138
134
 
139
- true
135
+ return true
140
136
  end
141
137
 
142
138
  def valid_event_type?(event_type)
@@ -155,7 +151,7 @@ module SplitIoClient
155
151
  return false
156
152
  end
157
153
 
158
- true
154
+ return true
159
155
  end
160
156
 
161
157
  def valid_traffic_type_name?(traffic_type_name)
@@ -174,7 +170,7 @@ module SplitIoClient
174
170
  return false
175
171
  end
176
172
 
177
- true
173
+ return true
178
174
  end
179
175
 
180
176
  def valid_value?(value)
@@ -183,7 +179,7 @@ module SplitIoClient
183
179
  return false
184
180
  end
185
181
 
186
- true
182
+ return true
187
183
  end
188
184
  end
189
185
  end
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '5.1.3.pre.rc4'
2
+ VERSION = '5.1.4.pre.rc1'
3
3
  end
@@ -17,6 +17,8 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|features|ext)/}) }
19
19
 
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
22
  spec.require_paths = ['lib']
21
23
 
22
24
  if defined?(JRUBY_VERSION)
@@ -51,7 +53,7 @@ Gem::Specification.new do |spec|
51
53
  spec.add_runtime_dependency 'faraday', '>= 0.8'
52
54
  spec.add_runtime_dependency 'json', '>= 1.8'
53
55
  spec.add_runtime_dependency 'lru_redux'
54
- spec.add_runtime_dependency 'net-http-persistent', '>= 2.9'
56
+ spec.add_runtime_dependency 'net-http-persistent', '~> 3.0'
55
57
  spec.add_runtime_dependency 'redis', '>= 3.2'
56
58
  spec.add_runtime_dependency 'thread_safe', '>= 0.3'
57
59
  end
@@ -0,0 +1,7 @@
1
+ ---
2
+ :api_key: api_key
3
+ :mode: :producer
4
+ :cache_adapter: :redis
5
+ :debug_enabled: true
6
+ :transport_debug_enabled: true
7
+ :impressions_refresh_rate: 30
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.3.pre.rc4
4
+ version: 5.1.4.pre.rc1
5
5
  platform: java
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-13 00:00:00.000000000 Z
11
+ date: 2018-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -223,17 +223,17 @@ dependencies:
223
223
  - !ruby/object:Gem::Dependency
224
224
  requirement: !ruby/object:Gem::Requirement
225
225
  requirements:
226
- - - ">="
226
+ - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: '2.9'
228
+ version: '3.0'
229
229
  name: net-http-persistent
230
230
  prerelease: false
231
231
  type: :runtime
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
- - - ">="
234
+ - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: '2.9'
236
+ version: '3.0'
237
237
  - !ruby/object:Gem::Dependency
238
238
  requirement: !ruby/object:Gem::Requirement
239
239
  requirements:
@@ -265,7 +265,8 @@ dependencies:
265
265
  description: Ruby client for using split SDK.
266
266
  email:
267
267
  - pato@split.io
268
- executables: []
268
+ executables:
269
+ - splitio
269
270
  extensions: []
270
271
  extra_rdoc_files: []
271
272
  files:
@@ -278,6 +279,7 @@ files:
278
279
  - NEWS
279
280
  - README.md
280
281
  - Rakefile
282
+ - exe/splitio
281
283
  - ext/murmurhash/MurmurHash3.java
282
284
  - lib/murmurhash/base.rb
283
285
  - lib/murmurhash/murmurhash.jar
@@ -361,6 +363,7 @@ files:
361
363
  - lib/splitclient-rb/validators.rb
362
364
  - lib/splitclient-rb/version.rb
363
365
  - splitclient-rb.gemspec
366
+ - splitio.yml.example
364
367
  - tasks/benchmark_get_treatment.rake
365
368
  - tasks/irb.rake
366
369
  - tasks/rspec.rake