splitclient-rb 4.5.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/CHANGES.txt +147 -0
- data/Detailed-README.md +571 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/NEWS +75 -0
- data/README.md +43 -0
- data/Rakefile +24 -0
- data/exe/splitio +96 -0
- data/ext/murmurhash/MurmurHash3.java +162 -0
- data/lib/murmurhash/base.rb +58 -0
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/murmurhash/murmurhash_mri.rb +3 -0
- data/lib/splitclient-rb.rb +90 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +133 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +44 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +165 -0
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +30 -0
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +29 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +41 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +49 -0
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +78 -0
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +21 -0
- data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +129 -0
- data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +98 -0
- data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +22 -0
- data/lib/splitclient-rb/cache/repositories/repository.rb +23 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +82 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +106 -0
- data/lib/splitclient-rb/cache/routers/impression_router.rb +52 -0
- data/lib/splitclient-rb/cache/senders/events_sender.rb +47 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +73 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +67 -0
- data/lib/splitclient-rb/cache/senders/metrics_sender.rb +49 -0
- data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +48 -0
- data/lib/splitclient-rb/cache/stores/segment_store.rb +82 -0
- data/lib/splitclient-rb/cache/stores/split_store.rb +97 -0
- data/lib/splitclient-rb/clients/localhost_split_client.rb +92 -0
- data/lib/splitclient-rb/clients/split_client.rb +214 -0
- data/lib/splitclient-rb/engine/api/client.rb +74 -0
- data/lib/splitclient-rb/engine/api/events.rb +48 -0
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +55 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +42 -0
- data/lib/splitclient-rb/engine/api/metrics.rb +61 -0
- data/lib/splitclient-rb/engine/api/segments.rb +62 -0
- data/lib/splitclient-rb/engine/api/splits.rb +60 -0
- data/lib/splitclient-rb/engine/evaluator/splitter.rb +123 -0
- data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +46 -0
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +56 -0
- data/lib/splitclient-rb/engine/matchers/combiners.rb +9 -0
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +86 -0
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +21 -0
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +19 -0
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +30 -0
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +27 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +54 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +19 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +53 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +53 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +24 -0
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +60 -0
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +23 -0
- data/lib/splitclient-rb/engine/matchers/set_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +45 -0
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +128 -0
- data/lib/splitclient-rb/engine/metrics/metrics.rb +83 -0
- data/lib/splitclient-rb/engine/models/label.rb +8 -0
- data/lib/splitclient-rb/engine/models/split.rb +17 -0
- data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +210 -0
- data/lib/splitclient-rb/engine/parser/evaluator.rb +118 -0
- data/lib/splitclient-rb/engine/parser/partition.rb +35 -0
- data/lib/splitclient-rb/engine/parser/split_adapter.rb +88 -0
- data/lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb +4 -0
- data/lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb +4 -0
- data/lib/splitclient-rb/localhost_split_factory.rb +13 -0
- 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 +391 -0
- data/lib/splitclient-rb/split_factory.rb +35 -0
- data/lib/splitclient-rb/split_factory_builder.rb +16 -0
- data/lib/splitclient-rb/utilitites.rb +41 -0
- data/lib/splitclient-rb/version.rb +3 -0
- data/splitclient-rb.gemspec +50 -0
- data/splitio.yml.example +7 -0
- data/tasks/benchmark_get_treatment.rake +43 -0
- data/tasks/irb.rake +4 -0
- data/tasks/rspec.rake +3 -0
- metadata +321 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
module Repositories
|
|
4
|
+
# Repository which forwards impressions interface to the selected adapter
|
|
5
|
+
class MetricsRepository < Repository
|
|
6
|
+
extend Forwardable
|
|
7
|
+
def_delegators :@adapter, :add_count, :add_latency, :add_gauge, :counts, :latencies, :gauges,
|
|
8
|
+
:clear_counts, :clear_latencies, :clear_gauges, :clear
|
|
9
|
+
|
|
10
|
+
def initialize(adapter, config)
|
|
11
|
+
@config = config
|
|
12
|
+
@adapter = case adapter.class.to_s
|
|
13
|
+
when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
|
|
14
|
+
Repositories::Metrics::MemoryRepository.new(adapter, config)
|
|
15
|
+
when 'SplitIoClient::Cache::Adapters::RedisAdapter'
|
|
16
|
+
Repositories::Metrics::RedisRepository.new(adapter, config)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
class Repository
|
|
4
|
+
def set_string(key, str)
|
|
5
|
+
@adapter.set_string(namespace_key(key), str)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def string(key)
|
|
9
|
+
@adapter.string(namespace_key(key))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
protected
|
|
13
|
+
|
|
14
|
+
def namespace_key(key = '')
|
|
15
|
+
"#{@config.redis_namespace}#{key}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def impressions_metrics_key(key)
|
|
19
|
+
namespace_key("/#{@config.language}-#{@config.version}/#{@config.machine_ip}/#{key}")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
module Repositories
|
|
4
|
+
class SegmentsRepository < Repository
|
|
5
|
+
KEYS_SLICE = 3000
|
|
6
|
+
|
|
7
|
+
attr_reader :adapter
|
|
8
|
+
|
|
9
|
+
def initialize(adapter, config)
|
|
10
|
+
@adapter = adapter
|
|
11
|
+
@config = config
|
|
12
|
+
|
|
13
|
+
@adapter.set_bool(namespace_key('.ready'), false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Receives segment data, adds and removes segements from the store
|
|
17
|
+
def add_to_segment(segment)
|
|
18
|
+
name = segment[:name]
|
|
19
|
+
|
|
20
|
+
@adapter.initialize_set(segment_data(name)) unless @adapter.exists?(segment_data(name))
|
|
21
|
+
|
|
22
|
+
add_keys(name, segment[:added])
|
|
23
|
+
remove_keys(name, segment[:removed])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get_segment_keys(name)
|
|
27
|
+
@adapter.get_set(segment_data(name))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def in_segment?(name, key)
|
|
31
|
+
@adapter.in_set?(segment_data(name), key)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def used_segment_names
|
|
35
|
+
@adapter.get_set(namespace_key('.segments.registered'))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def set_change_number(name, last_change)
|
|
39
|
+
@adapter.set_string(namespace_key(".segment.#{name}.till"), last_change)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_change_number(name)
|
|
43
|
+
@adapter.string(namespace_key(".segment.#{name}.till")) || -1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ready?
|
|
47
|
+
@adapter.string(namespace_key('.segments.ready')).to_i != -1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def not_ready!
|
|
51
|
+
@adapter.set_string(namespace_key('.segments.ready'), -1)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ready!
|
|
55
|
+
@adapter.set_string(namespace_key('.segments.ready'), Time.now.utc.to_i)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def clear
|
|
59
|
+
@adapter.clear(namespace_key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def segment_data(name)
|
|
65
|
+
namespace_key(".segment.#{name}")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def add_keys(name, keys)
|
|
69
|
+
keys.each_slice(KEYS_SLICE) do |keys_slice|
|
|
70
|
+
@adapter.add_to_set(segment_data(name), keys_slice)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def remove_keys(name, keys)
|
|
75
|
+
keys.each_slice(KEYS_SLICE) do |keys_slice|
|
|
76
|
+
@adapter.delete_from_set(segment_data(name), keys_slice)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
require 'concurrent'
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Cache
|
|
5
|
+
module Repositories
|
|
6
|
+
class SplitsRepository < Repository
|
|
7
|
+
SPLITS_SLICE = 10
|
|
8
|
+
|
|
9
|
+
attr_reader :adapter
|
|
10
|
+
|
|
11
|
+
def initialize(adapter, config)
|
|
12
|
+
@adapter = adapter
|
|
13
|
+
@config = config
|
|
14
|
+
|
|
15
|
+
@adapter.set_string(namespace_key('.splits.till'), '-1')
|
|
16
|
+
@adapter.initialize_map(namespace_key('.segments.registered'))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_split(split)
|
|
20
|
+
return unless split[:name]
|
|
21
|
+
|
|
22
|
+
@adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def remove_split(name)
|
|
26
|
+
@adapter.delete(namespace_key(".split.#{name}"))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_splits(names, slice = SPLITS_SLICE)
|
|
30
|
+
splits = {}
|
|
31
|
+
|
|
32
|
+
names.each_slice(slice) do |splits_slice|
|
|
33
|
+
splits.merge!(
|
|
34
|
+
@adapter
|
|
35
|
+
.multiple_strings(splits_slice.map { |name| namespace_key(".split.#{name}") })
|
|
36
|
+
.map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
splits.map do |name, data|
|
|
41
|
+
parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
|
|
42
|
+
[name.to_sym, parsed_data]
|
|
43
|
+
end.to_h
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_split(name)
|
|
47
|
+
split = @adapter.string(namespace_key(".split.#{name}"))
|
|
48
|
+
|
|
49
|
+
JSON.parse(split, symbolize_names: true) if split
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def splits
|
|
53
|
+
splits_hash = {}
|
|
54
|
+
|
|
55
|
+
split_names.each do |name|
|
|
56
|
+
splits_hash[name] = get_split(name)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
splits_hash
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Return an array of Split Names excluding control keys like splits.till
|
|
63
|
+
def split_names
|
|
64
|
+
@adapter.find_strings_by_prefix(namespace_key('.split.'))
|
|
65
|
+
.map { |split| split.gsub(namespace_key('.split.'), '') }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def set_change_number(since)
|
|
69
|
+
@adapter.set_string(namespace_key('.splits.till'), since)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def get_change_number
|
|
73
|
+
@adapter.string(namespace_key('.splits.till'))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def set_segment_names(names)
|
|
77
|
+
return if names.nil? || names.empty?
|
|
78
|
+
|
|
79
|
+
names.each do |name|
|
|
80
|
+
@adapter.add_to_set(namespace_key('.segments.registered'), name)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def exists?(name)
|
|
85
|
+
@adapter.exists?(namespace_key(".split.#{name}"))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ready?
|
|
89
|
+
@adapter.string(namespace_key('.splits.ready')).to_i != -1
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def not_ready!
|
|
93
|
+
@adapter.set_string(namespace_key('.splits.ready'), -1)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ready!
|
|
97
|
+
@adapter.set_string(namespace_key('.splits.ready'), Time.now.utc.to_i)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def clear
|
|
101
|
+
@adapter.clear(namespace_key)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
class ImpressionRouter
|
|
3
|
+
attr_reader :router_thread
|
|
4
|
+
|
|
5
|
+
def initialize(config)
|
|
6
|
+
@config = config
|
|
7
|
+
@listener = config.impression_listener
|
|
8
|
+
@queue = Queue.new
|
|
9
|
+
router_thread
|
|
10
|
+
|
|
11
|
+
if defined?(PhusionPassenger)
|
|
12
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
|
13
|
+
router_thread if forked
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add(impression)
|
|
19
|
+
@queue.push(impression)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_bulk(impressions)
|
|
23
|
+
impressions[:split_names].each do |split_name|
|
|
24
|
+
@queue.push(
|
|
25
|
+
split_name: split_name.to_s,
|
|
26
|
+
matching_key: impressions[:matching_key],
|
|
27
|
+
bucketing_key: impressions[:bucketing_key],
|
|
28
|
+
treatment: {
|
|
29
|
+
label: impressions[:treatments_labels_change_numbers][split_name.to_sym][:label],
|
|
30
|
+
treatment: impressions[:treatments_labels_change_numbers][split_name.to_sym][:treatment],
|
|
31
|
+
change_number: impressions[:treatments_labels_change_numbers][split_name.to_sym][:change_number]
|
|
32
|
+
},
|
|
33
|
+
attributes: impressions[:attributes]
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def router_thread
|
|
41
|
+
@config.threads[:impression_router] = Thread.new do
|
|
42
|
+
loop do
|
|
43
|
+
begin
|
|
44
|
+
@listener.log(@queue.pop)
|
|
45
|
+
rescue StandardError => error
|
|
46
|
+
@config.log_found_exception(__method__.to_s, error)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
module Senders
|
|
4
|
+
class EventsSender
|
|
5
|
+
def initialize(events_repository, config, api_key)
|
|
6
|
+
@events_repository = events_repository
|
|
7
|
+
@config = config
|
|
8
|
+
@api_key = api_key
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
if ENV['SPLITCLIENT_ENV'] == 'test'
|
|
13
|
+
post_events
|
|
14
|
+
else
|
|
15
|
+
events_thread
|
|
16
|
+
|
|
17
|
+
if defined?(PhusionPassenger)
|
|
18
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
|
19
|
+
events_thread if forked
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def events_thread
|
|
28
|
+
@config.threads[:events_sender] = Thread.new do
|
|
29
|
+
@config.logger.info('Starting events service')
|
|
30
|
+
|
|
31
|
+
loop do
|
|
32
|
+
post_events
|
|
33
|
+
|
|
34
|
+
sleep(SplitIoClient::Utilities.randomize_interval(@config.events_push_rate))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def post_events
|
|
40
|
+
SplitIoClient::Api::Events.new(@api_key, @config, @events_repository.clear).post
|
|
41
|
+
rescue StandardError => error
|
|
42
|
+
@config.log_found_exception(__method__.to_s, error)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
module Senders
|
|
4
|
+
class ImpressionsFormatter
|
|
5
|
+
def initialize(impressions_repository)
|
|
6
|
+
@impressions_repository = impressions_repository
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(raw_impressions)
|
|
10
|
+
impressions = raw_impressions ? raw_impressions : @impressions_repository.clear
|
|
11
|
+
formatted_impressions = []
|
|
12
|
+
filtered_impressions = filter_impressions(impressions)
|
|
13
|
+
|
|
14
|
+
return [] if impressions.empty? || filtered_impressions.empty?
|
|
15
|
+
|
|
16
|
+
formatted_impressions = unique_features(filtered_impressions).each_with_object([]) do |feature, memo|
|
|
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
|
+
|
|
33
|
+
memo << {
|
|
34
|
+
testName: feature.to_sym,
|
|
35
|
+
keyImpressions: current_impressions,
|
|
36
|
+
ip: ip
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
formatted_impressions
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def unique_features(impressions)
|
|
46
|
+
impressions.map { |impression| impression[:feature] }.uniq
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Filter seen impressions by impression_hash
|
|
50
|
+
def filter_impressions(unfiltered_impressions)
|
|
51
|
+
impressions_seen = []
|
|
52
|
+
|
|
53
|
+
unfiltered_impressions.each_with_object([]) do |impression, impressions|
|
|
54
|
+
impression_hash = impression_hash(impression)
|
|
55
|
+
|
|
56
|
+
next if impressions_seen.include?(impression_hash)
|
|
57
|
+
|
|
58
|
+
impressions_seen << impression_hash
|
|
59
|
+
impressions << impression
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def impression_hash(impression)
|
|
64
|
+
"#{impression[:feature]}:" \
|
|
65
|
+
"#{impression[:impressions]['keyName']}:" \
|
|
66
|
+
"#{impression[:impressions]['bucketingKey']}:" \
|
|
67
|
+
"#{impression[:impressions]['changeNumber']}:" \
|
|
68
|
+
"#{impression[:impressions]['treatment']}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
module Cache
|
|
3
|
+
module Senders
|
|
4
|
+
class ImpressionsSender
|
|
5
|
+
def initialize(impressions_repository, config, api_key)
|
|
6
|
+
@impressions_repository = impressions_repository
|
|
7
|
+
@config = config
|
|
8
|
+
@api_key = api_key
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
# Disable impressions if @config.impressions_queue_size == -1
|
|
13
|
+
if @config.impressions_queue_size < 0
|
|
14
|
+
@config.logger.info('Disabling impressions service by config')
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if ENV['SPLITCLIENT_ENV'] == 'test'
|
|
19
|
+
post_impressions
|
|
20
|
+
else
|
|
21
|
+
impressions_thread
|
|
22
|
+
|
|
23
|
+
if defined?(PhusionPassenger)
|
|
24
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
|
25
|
+
impressions_thread if forked
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def impressions_thread
|
|
34
|
+
@config.threads[:impressions_sender] = Thread.new do
|
|
35
|
+
begin
|
|
36
|
+
@config.logger.info('Starting impressions service')
|
|
37
|
+
|
|
38
|
+
loop do
|
|
39
|
+
post_impressions
|
|
40
|
+
|
|
41
|
+
sleep(SplitIoClient::Utilities.randomize_interval(@config.impressions_refresh_rate))
|
|
42
|
+
end
|
|
43
|
+
rescue SplitIoClient::ImpressionShutdownException
|
|
44
|
+
post_impressions
|
|
45
|
+
|
|
46
|
+
@impressions_repository.clear
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def post_impressions
|
|
52
|
+
impressions_client.post
|
|
53
|
+
rescue StandardError => error
|
|
54
|
+
@config.log_found_exception(__method__.to_s, error)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def formatted_impressions(raw_impressions = nil)
|
|
58
|
+
ImpressionsFormatter.new(@impressions_repository).call(raw_impressions)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def impressions_client
|
|
62
|
+
SplitIoClient::Api::Impressions.new(@api_key, @config, formatted_impressions)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|