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.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf6a979569b37d8f4fbdb3dd072199b1602c33b0
|
4
|
+
data.tar.gz: f21c0cb168466ae3ff5e7b2939d738e0ef0bf32b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e06b3667ff3ec8e06d5abaf20bb25a128f01b9b9a1c784bbb1ab720b35e3d911cbbe132702b9789a6d77793612d966b67df0e2caa0d60fc236341a8ec475ff53
|
7
|
+
data.tar.gz: 97f58dabae2e49fed331bb0d8db5d305ca873a5737e5c6efefc31c63b1bb9e5945f93b168708a0f50922810130875429ca56fe9f18dcb1e21756f2389f5ea8a9
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
3.1.4
|
2
|
+
- Allow to store block until ready flag in Redis
|
3
|
+
|
4
|
+
3.1.3
|
5
|
+
- Refactor SplitFactory - split it into separate mangers and client classes
|
6
|
+
- Refactor Utilities to comply style guide
|
7
|
+
|
1
8
|
3.1.2
|
2
9
|
- Fix issue with complex key where get_treatment and get_treatments return different values.
|
3
10
|
|
data/Detailed-README.md
CHANGED
@@ -76,13 +76,18 @@ new-navigation v3
|
|
76
76
|
To use SDK in the localhost mode you should pass `localhost` as an API key like this:
|
77
77
|
|
78
78
|
```ruby
|
79
|
-
factory
|
79
|
+
factory = SplitIoClient::SplitFactoryBuilder.build('localhost', path: '/where/to-look-for/<file_name>')
|
80
80
|
split_client = factory.client
|
81
81
|
```
|
82
82
|
|
83
|
-
By default SDK will look in your home directory (i.e. `~`) for a `.split` file, but you can specify a different
|
83
|
+
By default SDK will look in your home directory (i.e. `~`) for a `.split` file, but you can specify a different
|
84
84
|
file name (full path) to look for the file (note: you must provide absolute path):
|
85
85
|
|
86
|
+
When in localhost mode you can make use of the SDK ability to automatically refresh splits from file, to do that just specify reload rate in seconds like this:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
factory = SplitIoClient::SplitFactoryBuilder.build('localhost', path: '/where/to-look-for/<file_name>', reload_rate: 3)
|
90
|
+
```
|
86
91
|
|
87
92
|
### Ruby on Rails
|
88
93
|
---
|
@@ -327,6 +332,7 @@ SDK can be ran in `producer` mode both in the scope of the application (e.g. as
|
|
327
332
|
:redis_url: 'redis://127.0.0.1:6379/0'
|
328
333
|
```
|
329
334
|
|
335
|
+
|
330
336
|
- Install binstubs
|
331
337
|
```ruby
|
332
338
|
bundle binstubs splitclient-rb
|
@@ -337,7 +343,15 @@ bundle binstubs splitclient-rb
|
|
337
343
|
bundle exec bin/splitio -c ~/path/to/config/file.yml
|
338
344
|
```
|
339
345
|
|
340
|
-
|
346
|
+
Also, you can pass options directly to the cli command, like this:
|
347
|
+
```
|
348
|
+
bundle exec bin/splitio -c ~/path/to/config/file.yml --debug
|
349
|
+
```
|
350
|
+
|
351
|
+
Note: options passed through cli have higher priority than those specified in the configuration file. To see the full list of supported options you can run:
|
352
|
+
```
|
353
|
+
bundle exec bin/splitio -h
|
354
|
+
```
|
341
355
|
|
342
356
|
## Development
|
343
357
|
|
@@ -349,10 +363,11 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
349
363
|
|
350
364
|
The gem uses rspec for unit testing. Under the default `/spec` folder you will find the files for the unit tests and the specs helper file ( spec_helper.rb ). If a new spec file with new unit tests is required you just simply need to create it under the spec folder and all its test will be executed on the next rspec execution.
|
351
365
|
|
352
|
-
To run the suite of unit tests a rake task is provided.
|
366
|
+
To run the suite of unit tests a rake task is provided.
|
353
367
|
|
368
|
+
Make sure redis is running in localhost at redis://127.0.0.1:6379/0 and then just run:
|
354
369
|
```bash
|
355
|
-
|
370
|
+
SPLITCLIENT_ENV=test bundle exec rspecrake spec
|
356
371
|
```
|
357
372
|
|
358
373
|
Also, simplecov is used for coverage reporting. After the execution of the rake task it will create the `/coverage` folder with coverage reports in pretty HTML format.
|
data/exe/splitio
CHANGED
@@ -9,12 +9,61 @@ require_relative '../lib/splitclient-rb'
|
|
9
9
|
|
10
10
|
ARGV << '-h' if ARGV.empty?
|
11
11
|
|
12
|
+
config_path = ''
|
12
13
|
options = {}
|
13
14
|
opt_parser = OptionParser.new do |opts|
|
14
15
|
opts.banner = "Usage: splitio [options]"
|
15
16
|
|
16
17
|
opts.on("-cPATH", "--config=PATH", "Set the path to splitio.yml config file") do |c|
|
17
|
-
|
18
|
+
config_path = c
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("--base-uri=BASE_URI", "Set the base uri for Split SDK") do |c|
|
22
|
+
options[:base_uri] = c
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("--events-uri=EVENTS_URI", "Set the events uri for Split SDK") do |c|
|
26
|
+
options[:events_uri] = c
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("--read-timeout=READ_TIMEOUT", "Read timeout in seconds") do |c|
|
30
|
+
options[:read_timeout] = c
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("--connection-timeout=CONNECTION_TIMEOUT", "Connection timeout in seconds") do |c|
|
34
|
+
options[:connection_timeout] = c
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("--features-refresh-rate=FEATURES_REFRESH_RATE", "Features refresh rate in seconds") do |c|
|
38
|
+
options[:features_refresh_rate] = c
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("--segments-refresh-rate=SEGMENTS_REFRESH_RATE", "Segments refresh rate in seconds") do |c|
|
42
|
+
options[:segments_refresh_rate] = c
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("--metrics-refresh-rate=METRICS_REFRESH_RATE", "Metrics refresh rate in seconds") do |c|
|
46
|
+
options[:metrics_refresh_rate] = c
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("--impressions-refresh-rate=IMPRESSIONS_REFRESH_RATE", "Impressions refresh rate in seconds") do |c|
|
50
|
+
options[:impressions_refresh_rate] = c
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("--block-until-ready=SECONDS", "Seconds to block the app until SDK is ready or false to run in non-blocking mode") do |c|
|
54
|
+
options[:block_until_ready] = c
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--redis-url=REDIS_URL", "Set base uri for Split SDK") do |c|
|
58
|
+
options[:redis_url] = c
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--transport-debug", "Enable transport debug") do
|
62
|
+
options[:transport_debug_enabled] = true
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("-d", "--debug", "Enable debug mode") do
|
66
|
+
options[:debug_enabled] = true
|
18
67
|
end
|
19
68
|
|
20
69
|
opts.on_tail("-h", "--help", "Prints this help") do
|
@@ -31,7 +80,9 @@ begin
|
|
31
80
|
exit(1)
|
32
81
|
end
|
33
82
|
|
34
|
-
config = YAML.load_file(
|
35
|
-
config
|
83
|
+
config = YAML.load_file(config_path)
|
84
|
+
config
|
85
|
+
.merge!(mode: :producer, cache_adapter: :redis)
|
86
|
+
.merge!(options)
|
36
87
|
|
37
88
|
SplitIoClient::SplitFactory.new(config[:api_key], config)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'redis'
|
2
1
|
require 'json'
|
3
2
|
|
4
3
|
module SplitIoClient
|
@@ -121,6 +120,12 @@ module SplitIoClient
|
|
121
120
|
def inc(key, inc = 1)
|
122
121
|
@redis.incrby(key, inc)
|
123
122
|
end
|
123
|
+
|
124
|
+
def pipelined(&block)
|
125
|
+
@redis.pipelined do
|
126
|
+
block.call
|
127
|
+
end
|
128
|
+
end
|
124
129
|
end
|
125
130
|
end
|
126
131
|
end
|
@@ -18,9 +18,15 @@ module SplitIoClient
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def add_bulk(key, treatments, time)
|
21
|
+
def add_bulk(key, bucketing_key, treatments, time)
|
22
22
|
treatments.each do |split_name, treatment|
|
23
|
-
add(
|
23
|
+
add(
|
24
|
+
split_name,
|
25
|
+
'key_name' => key,
|
26
|
+
'bucketing_key' => bucketing_key,
|
27
|
+
'treatment' => treatment,
|
28
|
+
'time' => time
|
29
|
+
)
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
@@ -17,11 +17,11 @@ module SplitIoClient
|
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
20
|
-
def add_bulk(key, treatments, time)
|
20
|
+
def add_bulk(key, bucketing_key, treatments, time)
|
21
21
|
@adapter.redis.pipelined do
|
22
22
|
treatments.each_slice(IMPRESSIONS_SLICE) do |treatments_slice|
|
23
23
|
treatments_slice.each do |split_name, treatment|
|
24
|
-
add(split_name, 'key_name' => key, 'treatment' => treatment, 'time' => time)
|
24
|
+
add(split_name, 'key_name' => key, 'bucketing_key' => bucketing_key, 'treatment' => treatment, 'time' => time)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module SplitIoClient
|
2
4
|
module Cache
|
3
5
|
module Repositories
|
@@ -7,6 +9,7 @@ module SplitIoClient
|
|
7
9
|
def_delegators :@adapter, :add, :add_bulk, :clear, :empty?
|
8
10
|
|
9
11
|
def initialize(adapter, config)
|
12
|
+
@config = config
|
10
13
|
@adapter = case adapter.class.to_s
|
11
14
|
when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
|
12
15
|
Repositories::Impressions::MemoryRepository.new(adapter, config)
|
@@ -8,6 +8,7 @@ module SplitIoClient
|
|
8
8
|
:clear_counts, :clear_latencies, :clear_gauges
|
9
9
|
|
10
10
|
def initialize(adapter, config)
|
11
|
+
@config = config
|
11
12
|
@adapter = case adapter.class.to_s
|
12
13
|
when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
|
13
14
|
Repositories::Metrics::MemoryRepository.new(adapter, config)
|
@@ -4,8 +4,11 @@ module SplitIoClient
|
|
4
4
|
class SegmentsRepository < Repository
|
5
5
|
KEYS_SLICE = 3000
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader :adapter
|
8
|
+
|
9
|
+
def initialize(adapter, config)
|
8
10
|
@adapter = adapter
|
11
|
+
@config = config
|
9
12
|
|
10
13
|
@adapter.set_bool(namespace_key('ready'), false)
|
11
14
|
end
|
@@ -40,6 +43,18 @@ module SplitIoClient
|
|
40
43
|
@adapter.string(namespace_key("segment.#{name}.till")) || -1
|
41
44
|
end
|
42
45
|
|
46
|
+
def ready?
|
47
|
+
@adapter.string(namespace_key('cache.ready.segments')).to_i != -1
|
48
|
+
end
|
49
|
+
|
50
|
+
def not_ready!
|
51
|
+
@adapter.set_string(namespace_key('cache.ready.segments'), -1)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ready!
|
55
|
+
@adapter.set_string(namespace_key('cache.ready.segments'), Time.now.utc.to_i)
|
56
|
+
end
|
57
|
+
|
43
58
|
private
|
44
59
|
|
45
60
|
def segment_data(name)
|
@@ -6,10 +6,13 @@ module SplitIoClient
|
|
6
6
|
class SplitsRepository < Repository
|
7
7
|
SPLITS_SLICE = 10
|
8
8
|
|
9
|
-
|
9
|
+
attr_reader :adapter
|
10
|
+
|
11
|
+
def initialize(adapter, config)
|
10
12
|
@adapter = adapter
|
13
|
+
@config = config
|
11
14
|
|
12
|
-
@adapter.set_string(namespace_key('
|
15
|
+
@adapter.set_string(namespace_key('splits.till'), '-1')
|
13
16
|
@adapter.initialize_map(namespace_key('segments.registered'))
|
14
17
|
end
|
15
18
|
|
@@ -54,19 +57,18 @@ module SplitIoClient
|
|
54
57
|
splits_hash
|
55
58
|
end
|
56
59
|
|
57
|
-
# Return an array of Split Names excluding control keys like
|
60
|
+
# Return an array of Split Names excluding control keys like splits.till
|
58
61
|
def split_names
|
59
|
-
@adapter.find_strings_by_prefix(namespace_key('split'))
|
60
|
-
.reject { |split| split == namespace_key('split.till') }
|
62
|
+
@adapter.find_strings_by_prefix(namespace_key('split.'))
|
61
63
|
.map { |split| split.gsub(namespace_key('split.'), '') }
|
62
64
|
end
|
63
65
|
|
64
66
|
def set_change_number(since)
|
65
|
-
@adapter.set_string(namespace_key('
|
67
|
+
@adapter.set_string(namespace_key('splits.till'), since)
|
66
68
|
end
|
67
69
|
|
68
70
|
def get_change_number
|
69
|
-
@adapter.string(namespace_key('
|
71
|
+
@adapter.string(namespace_key('splits.till'))
|
70
72
|
end
|
71
73
|
|
72
74
|
def set_segment_names(names)
|
@@ -80,6 +82,18 @@ module SplitIoClient
|
|
80
82
|
def exists?(name)
|
81
83
|
@adapter.exists?(namespace_key("split.#{name}"))
|
82
84
|
end
|
85
|
+
|
86
|
+
def ready?
|
87
|
+
@adapter.string(namespace_key('cache.ready.splits')).to_i != -1
|
88
|
+
end
|
89
|
+
|
90
|
+
def not_ready!
|
91
|
+
@adapter.set_string(namespace_key('cache.ready.splits'), -1)
|
92
|
+
end
|
93
|
+
|
94
|
+
def ready!
|
95
|
+
@adapter.set_string(namespace_key('cache.ready.splits'), Time.now.utc.to_i)
|
96
|
+
end
|
83
97
|
end
|
84
98
|
end
|
85
99
|
end
|
@@ -18,13 +18,11 @@ module SplitIoClient
|
|
18
18
|
if ENV['SPLITCLIENT_ENV'] == 'test'
|
19
19
|
post_impressions
|
20
20
|
else
|
21
|
-
|
22
|
-
@config.logger.info('Starting impressions service')
|
21
|
+
impressions_thread
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
sleep(randomize_interval(@config.impressions_refresh_rate))
|
23
|
+
if defined?(PhusionPassenger)
|
24
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
25
|
+
impressions_thread if forked
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
@@ -32,6 +30,18 @@ module SplitIoClient
|
|
32
30
|
|
33
31
|
private
|
34
32
|
|
33
|
+
def impressions_thread
|
34
|
+
Thread.new do
|
35
|
+
@config.logger.info('Starting impressions service')
|
36
|
+
|
37
|
+
loop do
|
38
|
+
post_impressions
|
39
|
+
|
40
|
+
sleep(::Utilities.randomize_interval(@config.impressions_refresh_rate))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
35
45
|
def post_impressions
|
36
46
|
impressions_client.post
|
37
47
|
rescue StandardError => error
|
@@ -45,12 +55,6 @@ module SplitIoClient
|
|
45
55
|
def impressions_client
|
46
56
|
SplitIoClient::Api::Impressions.new(@api_key, @config, formatted_impressions)
|
47
57
|
end
|
48
|
-
|
49
|
-
def randomize_interval(interval)
|
50
|
-
@random_generator ||= Random.new
|
51
|
-
random_factor = @random_generator.rand(50..100)/100.0
|
52
|
-
interval * random_factor
|
53
|
-
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Cache
|
3
|
+
module Senders
|
4
|
+
class MetricsSender
|
5
|
+
def initialize(metrics_repository, config, api_key)
|
6
|
+
@metrics_repository = metrics_repository
|
7
|
+
@config = config
|
8
|
+
@api_key = api_key
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
return if ENV['SPLITCLIENT_ENV'] == 'test'
|
13
|
+
|
14
|
+
post_metrics
|
15
|
+
|
16
|
+
Thread.new do
|
17
|
+
@config.logger.info('Starting metrics service')
|
18
|
+
|
19
|
+
loop do
|
20
|
+
post_metrics
|
21
|
+
|
22
|
+
sleep(::Utilities.randomize_interval(@config.metrics_refresh_rate))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def post_metrics
|
30
|
+
metrics_client.post
|
31
|
+
rescue StandardError => error
|
32
|
+
@config.log_found_exception(__method__.to_s, error)
|
33
|
+
end
|
34
|
+
|
35
|
+
def metrics_client
|
36
|
+
SplitIoClient::Api::Metrics.new(@api_key, @config, @metrics_repository)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -5,29 +5,30 @@ module SplitIoClient
|
|
5
5
|
module Cache
|
6
6
|
module Stores
|
7
7
|
class SDKBlocker
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :splits_repository
|
9
9
|
attr_writer :splits_thread, :segments_thread
|
10
10
|
|
11
|
-
def initialize(config)
|
11
|
+
def initialize(config, splits_repository, segments_repository)
|
12
12
|
@config = config
|
13
|
+
@splits_repository = splits_repository
|
14
|
+
@segments_repository = segments_repository
|
13
15
|
|
14
|
-
@
|
15
|
-
@
|
16
|
+
@splits_repository.not_ready!
|
17
|
+
@segments_repository.not_ready!
|
16
18
|
end
|
17
19
|
|
18
20
|
def splits_ready!
|
19
|
-
@
|
21
|
+
@splits_repository.ready!
|
20
22
|
end
|
21
23
|
|
22
24
|
def segments_ready!
|
23
|
-
@
|
25
|
+
@segments_repository.ready!
|
24
26
|
end
|
25
27
|
|
26
28
|
def block
|
27
29
|
begin
|
28
30
|
Timeout::timeout(@config.block_until_ready) do
|
29
31
|
sleep 0.1 until ready?
|
30
|
-
sleep 0.1 until ready?
|
31
32
|
end
|
32
33
|
rescue Timeout::Error
|
33
34
|
fail SDKBlockerTimeoutExpiredException, 'SDK start up timeout expired'
|
@@ -39,7 +40,7 @@ module SplitIoClient
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def ready?
|
42
|
-
@
|
43
|
+
@splits_repository.ready? && @segments_repository.ready?
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|