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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/CHANGES.txt +147 -0
  4. data/Detailed-README.md +571 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +13 -0
  7. data/NEWS +75 -0
  8. data/README.md +43 -0
  9. data/Rakefile +24 -0
  10. data/exe/splitio +96 -0
  11. data/ext/murmurhash/MurmurHash3.java +162 -0
  12. data/lib/murmurhash/base.rb +58 -0
  13. data/lib/murmurhash/murmurhash.jar +0 -0
  14. data/lib/murmurhash/murmurhash_mri.rb +3 -0
  15. data/lib/splitclient-rb.rb +90 -0
  16. data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
  17. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +133 -0
  18. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +44 -0
  19. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +165 -0
  20. data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +30 -0
  21. data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +29 -0
  22. data/lib/splitclient-rb/cache/repositories/events_repository.rb +41 -0
  23. data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +49 -0
  24. data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +78 -0
  25. data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +21 -0
  26. data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +129 -0
  27. data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +98 -0
  28. data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +22 -0
  29. data/lib/splitclient-rb/cache/repositories/repository.rb +23 -0
  30. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +82 -0
  31. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +106 -0
  32. data/lib/splitclient-rb/cache/routers/impression_router.rb +52 -0
  33. data/lib/splitclient-rb/cache/senders/events_sender.rb +47 -0
  34. data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +73 -0
  35. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +67 -0
  36. data/lib/splitclient-rb/cache/senders/metrics_sender.rb +49 -0
  37. data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +48 -0
  38. data/lib/splitclient-rb/cache/stores/segment_store.rb +82 -0
  39. data/lib/splitclient-rb/cache/stores/split_store.rb +97 -0
  40. data/lib/splitclient-rb/clients/localhost_split_client.rb +92 -0
  41. data/lib/splitclient-rb/clients/split_client.rb +214 -0
  42. data/lib/splitclient-rb/engine/api/client.rb +74 -0
  43. data/lib/splitclient-rb/engine/api/events.rb +48 -0
  44. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +55 -0
  45. data/lib/splitclient-rb/engine/api/impressions.rb +42 -0
  46. data/lib/splitclient-rb/engine/api/metrics.rb +61 -0
  47. data/lib/splitclient-rb/engine/api/segments.rb +62 -0
  48. data/lib/splitclient-rb/engine/api/splits.rb +60 -0
  49. data/lib/splitclient-rb/engine/evaluator/splitter.rb +123 -0
  50. data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +46 -0
  51. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +56 -0
  52. data/lib/splitclient-rb/engine/matchers/combiners.rb +9 -0
  53. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +86 -0
  54. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +21 -0
  55. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +19 -0
  56. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +30 -0
  57. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +20 -0
  58. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +26 -0
  59. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +27 -0
  60. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +54 -0
  61. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +19 -0
  62. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +53 -0
  63. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +53 -0
  64. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +24 -0
  65. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +60 -0
  66. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +23 -0
  67. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +20 -0
  68. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +26 -0
  69. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +45 -0
  70. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
  71. data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +128 -0
  72. data/lib/splitclient-rb/engine/metrics/metrics.rb +83 -0
  73. data/lib/splitclient-rb/engine/models/label.rb +8 -0
  74. data/lib/splitclient-rb/engine/models/split.rb +17 -0
  75. data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
  76. data/lib/splitclient-rb/engine/parser/condition.rb +210 -0
  77. data/lib/splitclient-rb/engine/parser/evaluator.rb +118 -0
  78. data/lib/splitclient-rb/engine/parser/partition.rb +35 -0
  79. data/lib/splitclient-rb/engine/parser/split_adapter.rb +88 -0
  80. data/lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb +4 -0
  81. data/lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb +4 -0
  82. data/lib/splitclient-rb/localhost_split_factory.rb +13 -0
  83. data/lib/splitclient-rb/localhost_utils.rb +36 -0
  84. data/lib/splitclient-rb/managers/localhost_split_manager.rb +45 -0
  85. data/lib/splitclient-rb/managers/split_manager.rb +77 -0
  86. data/lib/splitclient-rb/split_config.rb +391 -0
  87. data/lib/splitclient-rb/split_factory.rb +35 -0
  88. data/lib/splitclient-rb/split_factory_builder.rb +16 -0
  89. data/lib/splitclient-rb/utilitites.rb +41 -0
  90. data/lib/splitclient-rb/version.rb +3 -0
  91. data/splitclient-rb.gemspec +50 -0
  92. data/splitio.yml.example +7 -0
  93. data/tasks/benchmark_get_treatment.rake +43 -0
  94. data/tasks/irb.rake +4 -0
  95. data/tasks/rspec.rake +3 -0
  96. metadata +321 -0
@@ -0,0 +1,49 @@
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
+ metrics_thread
15
+
16
+ if defined?(PhusionPassenger)
17
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
18
+ metrics_thread if forked
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def metrics_thread
26
+ @config.threads[:metrics_sender] = Thread.new do
27
+ @config.logger.info('Starting metrics service')
28
+
29
+ loop do
30
+ post_metrics
31
+
32
+ sleep(SplitIoClient::Utilities.randomize_interval(@config.metrics_refresh_rate))
33
+ end
34
+ end
35
+ end
36
+
37
+ def post_metrics
38
+ metrics_client.post
39
+ rescue StandardError => error
40
+ @config.log_found_exception(__method__.to_s, error)
41
+ end
42
+
43
+ def metrics_client
44
+ SplitIoClient::Api::Metrics.new(@api_key, @config, @metrics_repository)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,48 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ module SplitIoClient
5
+ module Cache
6
+ module Stores
7
+ class SDKBlocker
8
+ attr_reader :splits_repository
9
+ attr_writer :splits_thread, :segments_thread
10
+
11
+ def initialize(config, splits_repository, segments_repository)
12
+ @config = config
13
+ @splits_repository = splits_repository
14
+ @segments_repository = segments_repository
15
+
16
+ @splits_repository.not_ready!
17
+ @segments_repository.not_ready!
18
+ end
19
+
20
+ def splits_ready!
21
+ @splits_repository.ready!
22
+ end
23
+
24
+ def segments_ready!
25
+ @segments_repository.ready!
26
+ end
27
+
28
+ def block
29
+ begin
30
+ Timeout::timeout(@config.block_until_ready) do
31
+ sleep 0.1 until ready?
32
+ end
33
+ rescue Timeout::Error
34
+ fail SDKBlockerTimeoutExpiredException, 'SDK start up timeout expired'
35
+ end
36
+
37
+ @config.logger.info('SplitIO SDK is ready')
38
+ @splits_thread.run
39
+ @segments_thread.run
40
+ end
41
+
42
+ def ready?
43
+ @splits_repository.ready? && @segments_repository.ready?
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,82 @@
1
+ module SplitIoClient
2
+ module Cache
3
+ module Stores
4
+ class SegmentStore
5
+ attr_reader :segments_repository
6
+
7
+ def initialize(segments_repository, config, api_key, metrics, sdk_blocker = nil)
8
+ @segments_repository = segments_repository
9
+ @config = config
10
+ @api_key = api_key
11
+ @metrics = metrics
12
+ @sdk_blocker = sdk_blocker
13
+ end
14
+
15
+ def call
16
+ if ENV['SPLITCLIENT_ENV'] == 'test'
17
+ store_segments
18
+ else
19
+ segments_thread
20
+
21
+ if defined?(PhusionPassenger)
22
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
23
+ segments_thread if forked
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def segments_thread
32
+ @config.threads[:segment_store] = @sdk_blocker.segments_thread = Thread.new do
33
+ @config.logger.info('Starting segments fetcher service')
34
+ @config.block_until_ready > 0 ? blocked_store : unblocked_store
35
+ end
36
+ end
37
+
38
+ def blocked_store
39
+ loop do
40
+ next unless @sdk_blocker.splits_repository.ready?
41
+
42
+ store_segments
43
+ @config.logger.debug("Segment names: #{@segments_repository.used_segment_names.to_a}") if @config.debug_enabled
44
+
45
+ unless @sdk_blocker.ready?
46
+ @sdk_blocker.segments_ready!
47
+ @config.logger.info('segments are ready')
48
+ end
49
+
50
+ sleep_for = random_interval(@config.segments_refresh_rate)
51
+ @config.logger.debug("Segments store is sleeping for: #{sleep_for} seconds") if @config.debug_enabled
52
+ sleep(sleep_for)
53
+ end
54
+ end
55
+
56
+ def unblocked_store
57
+ loop do
58
+ store_segments
59
+
60
+ sleep(random_interval(@config.segments_refresh_rate))
61
+ end
62
+ end
63
+
64
+ def store_segments
65
+ segments_api.store_segments_by_names(@segments_repository.used_segment_names)
66
+ rescue StandardError => error
67
+ @config.log_found_exception(__method__.to_s, error)
68
+ end
69
+
70
+ def random_interval(interval)
71
+ random_factor = Random.new.rand(50..100) / 100.0
72
+
73
+ interval * random_factor
74
+ end
75
+
76
+ def segments_api
77
+ SplitIoClient::Api::Segments.new(@api_key, @config, @metrics, @segments_repository)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,97 @@
1
+ module SplitIoClient
2
+ module Cache
3
+ module Stores
4
+ class SplitStore
5
+ attr_reader :splits_repository
6
+
7
+ def initialize(splits_repository, config, api_key, metrics, sdk_blocker = nil)
8
+ @splits_repository = splits_repository
9
+ @config = config
10
+ @api_key = api_key
11
+ @metrics = metrics
12
+ @sdk_blocker = sdk_blocker
13
+ end
14
+
15
+ def call
16
+ if ENV['SPLITCLIENT_ENV'] == 'test'
17
+ store_splits
18
+ else
19
+ splits_thread
20
+
21
+ if defined?(PhusionPassenger)
22
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
23
+ splits_thread if forked
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def splits_thread
32
+ @config.threads[:split_store] = @sdk_blocker.splits_thread = Thread.new do
33
+ @config.logger.info('Starting splits fetcher service')
34
+ loop do
35
+ store_splits
36
+
37
+ sleep(random_interval(@config.features_refresh_rate))
38
+ end
39
+ end
40
+ end
41
+
42
+ def store_splits
43
+ data = splits_since(@splits_repository.get_change_number)
44
+
45
+ data[:splits] && data[:splits].each do |split|
46
+ add_split_unless_archived(split)
47
+ end
48
+
49
+ @splits_repository.set_segment_names(data[:segment_names])
50
+ @splits_repository.set_change_number(data[:till])
51
+
52
+ @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
53
+
54
+ if @config.block_until_ready > 0 && !@sdk_blocker.ready?
55
+ @sdk_blocker.splits_ready!
56
+ @config.logger.info('splits are ready')
57
+ end
58
+
59
+ rescue StandardError => error
60
+ @config.log_found_exception(__method__.to_s, error)
61
+ end
62
+
63
+ def random_interval(interval)
64
+ random_factor = Random.new.rand(50..100) / 100.0
65
+
66
+ interval * random_factor
67
+ end
68
+
69
+ def splits_since(since)
70
+ SplitIoClient::Api::Splits.new(@api_key, @config, @metrics).since(since)
71
+ end
72
+
73
+ def add_split_unless_archived(split)
74
+ if Engine::Models::Split.archived?(split)
75
+ @config.logger.debug("Seeing archived split #{split[:name]}") if @config.debug_enabled
76
+
77
+ remove_archived_split(split)
78
+ else
79
+ store_split(split)
80
+ end
81
+ end
82
+
83
+ def remove_archived_split(split)
84
+ @config.logger.debug("removing split from store(#{split})") if @config.debug_enabled
85
+
86
+ @splits_repository.remove_split(split[:name])
87
+ end
88
+
89
+ def store_split(split)
90
+ @config.logger.debug("storing split (#{split[:name]})") if @config.debug_enabled
91
+
92
+ @splits_repository.add_split(split)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,92 @@
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
+ 'ruby-'+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 SplitIoClient::Engine::Models::Treatment::CONTROL
48
+ end
49
+
50
+ unless feature
51
+ @config.logger.warn('feature was null for id: ' + id)
52
+ return SplitIoClient::Engine::Models::Treatment::CONTROL
53
+ end
54
+
55
+ result = get_localhost_treatment(feature)
56
+ end
57
+
58
+ def track
59
+ end
60
+
61
+ private
62
+
63
+ #
64
+ # auxiliary method to get the treatments avoding exceptions
65
+ #
66
+ # @param id [string] user id
67
+ # @param feature [string] name of the feature that is being validated
68
+ #
69
+ # @return [Treatment] tretment constant value
70
+ def get_treatment_without_exception_handling(id, feature, attributes = nil)
71
+ get_treatment(id, feature, attributes)
72
+ end
73
+
74
+ #
75
+ # method to check if the sdk is running in localhost mode based on api key
76
+ #
77
+ # @return [boolean] True if is in localhost mode, false otherwise
78
+ def is_localhost_mode?
79
+ true
80
+ end
81
+
82
+ #
83
+ # method to check the treatment for the given feature in localhost mode
84
+ #
85
+ # @return [boolean] true if the feature is available in localhost mode, false otherwise
86
+ def get_localhost_treatment(feature)
87
+ treatment = @localhost_mode_features.select { |h| h[:feature] == feature }.last || {}
88
+
89
+ treatment[:treatment] || SplitIoClient::Engine::Models::Treatment::CONTROL
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,214 @@
1
+ module SplitIoClient
2
+ class SplitClient
3
+ #
4
+ # Creates a new split client instance that connects to split.io API.
5
+ #
6
+ # @param api_key [String] the API key for your split account
7
+ #
8
+ # @return [SplitIoClient] split.io client instance
9
+ def initialize(api_key, config = {}, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository)
10
+ @config = config
11
+
12
+ @splits_repository = splits_repository
13
+ @segments_repository = segments_repository
14
+ @impressions_repository = impressions_repository
15
+ @metrics_repository = metrics_repository
16
+ @events_repository = events_repository
17
+
18
+ @adapter = adapter
19
+ end
20
+
21
+ def get_treatments(key, split_names, attributes = nil)
22
+ bucketing_key, matching_key = keys_from_key(key)
23
+ evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, true)
24
+
25
+ treatments_labels_change_numbers =
26
+ @splits_repository.get_splits(split_names).each_with_object({}) do |(name, data), memo|
27
+ memo.merge!(name => get_treatment(key, name, attributes, data, false, true, evaluator))
28
+ end
29
+
30
+ if @config.impressions_queue_size > 0
31
+ time = (Time.now.to_f * 1000.0).to_i
32
+ @impressions_repository.add_bulk(
33
+ matching_key, bucketing_key, treatments_labels_change_numbers, time
34
+ )
35
+
36
+ route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
37
+ end
38
+
39
+ split_names = treatments_labels_change_numbers.keys
40
+ treatments = treatments_labels_change_numbers.values.map { |v| v[:treatment] }
41
+
42
+ Hash[split_names.zip(treatments)]
43
+ end
44
+
45
+ #
46
+ # obtains the treatment for a given feature
47
+ #
48
+ # @param key [String/Hash] user id or hash with matching_key/bucketing_key
49
+ # @param split_name [String/Array] name of the feature that is being validated or array of them
50
+ # @param attributes [Hash] attributes to pass to the treatment class
51
+ # @param split_data [Hash] split data, when provided this method doesn't fetch splits_repository for the data
52
+ # @param store_impressions [Boolean] impressions aren't stored if this flag is false
53
+ # @param multiple [Hash] internal flag to signal if method is called by get_treatments
54
+ # @param evaluator [Evaluator] Evaluator class instance, used to cache treatments
55
+ #
56
+ # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
57
+ def get_treatment(
58
+ key, split_name, attributes = nil, split_data = nil, store_impressions = true,
59
+ multiple = false, evaluator = nil
60
+ )
61
+ bucketing_key, matching_key = keys_from_key(key)
62
+ treatment_data = { label: Engine::Models::Label::DEFINITION_NOT_FOUND, treatment: SplitIoClient::Engine::Models::Treatment::CONTROL }
63
+ evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository)
64
+
65
+ if matching_key.nil?
66
+ @config.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
67
+ return parsed_treatment(multiple, treatment_data)
68
+ end
69
+
70
+ if split_name.nil?
71
+ @config.logger.warn('split_name was null for key: ' + key)
72
+ return parsed_treatment(multiple, treatment_data)
73
+ end
74
+
75
+ start = Time.now
76
+
77
+ begin
78
+ split = multiple ? split_data : @splits_repository.get_split(split_name)
79
+
80
+ if split.nil?
81
+ @config.logger.debug("split_name: #{split_name} does not exist. Returning CONTROL")
82
+ return parsed_treatment(multiple, treatment_data)
83
+ else
84
+ treatment_data =
85
+ evaluator.call(
86
+ { bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
87
+ )
88
+ end
89
+ rescue StandardError => error
90
+ @config.log_found_exception(__method__.to_s, error)
91
+
92
+ store_impression(
93
+ split_name, matching_key, bucketing_key,
94
+ {
95
+ treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
96
+ label: SplitIoClient::Engine::Models::Label::EXCEPTION
97
+ },
98
+ store_impressions, attributes
99
+ )
100
+
101
+ return parsed_treatment(multiple, treatment_data)
102
+ end
103
+
104
+ begin
105
+ latency = (Time.now - start) * 1000.0
106
+ # Disable impressions if @config.impressions_queue_size == -1
107
+ split && store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions, attributes)
108
+
109
+ # Measure
110
+ @adapter.metrics.time('sdk.get_treatment', latency)
111
+ rescue StandardError => error
112
+ @config.log_found_exception(__method__.to_s, error)
113
+
114
+ store_impression(
115
+ split_name, matching_key, bucketing_key,
116
+ {
117
+ treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
118
+ label: SplitIoClient::Engine::Models::Label::EXCEPTION
119
+ },
120
+ store_impressions, attributes
121
+ )
122
+
123
+ return parsed_treatment(multiple, treatment_data)
124
+ end
125
+
126
+ parsed_treatment(multiple, treatment_data)
127
+ end
128
+
129
+ def destroy
130
+ @config.logger.info('Split client shutdown started...') if @config.debug_enabled
131
+
132
+ @config.threads[:impressions_sender].raise(SplitIoClient::ImpressionShutdownException)
133
+ @config.threads.reject { |k, _| k == :impressions_sender }.each do |name, thread|
134
+ Thread.kill(thread)
135
+ end
136
+
137
+ @metrics_repository.clear
138
+ @splits_repository.clear
139
+ @segments_repository.clear
140
+ @events_repository.clear
141
+
142
+ @config.logger.info('Split client shutdown complete') if @config.debug_enabled
143
+ end
144
+
145
+ def store_impression(split_name, matching_key, bucketing_key, treatment, store_impressions, attributes)
146
+ time = (Time.now.to_f * 1000.0).to_i
147
+ route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes) if @config.impression_listener && store_impressions
148
+
149
+ return if @config.impressions_queue_size <= 0 || !store_impressions
150
+
151
+ @impressions_repository.add(split_name,
152
+ 'keyName' => matching_key,
153
+ 'bucketingKey' => bucketing_key,
154
+ 'treatment' => treatment[:treatment],
155
+ 'label' => @config.labels_enabled ? treatment[:label] : nil,
156
+ 'time' => time,
157
+ 'changeNumber' => treatment[:change_number]
158
+ )
159
+ rescue StandardError => error
160
+ @config.log_found_exception(__method__.to_s, error)
161
+ end
162
+
163
+ def route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes)
164
+ impression_router.add(
165
+ split_name: split_name,
166
+ matching_key: matching_key,
167
+ bucketing_key: bucketing_key,
168
+ time: time,
169
+ treatment: treatment,
170
+ attributes: attributes
171
+ )
172
+ end
173
+
174
+ def route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
175
+ impression_router.add_bulk(
176
+ split_names: split_names,
177
+ matching_key: matching_key,
178
+ bucketing_key: bucketing_key,
179
+ time: time,
180
+ treatments_labels_change_numbers: treatments_labels_change_numbers,
181
+ attributes: attributes
182
+ )
183
+ end
184
+
185
+ def impression_router
186
+ @impression_router ||= SplitIoClient::ImpressionRouter.new(@config)
187
+ end
188
+
189
+ def track(key, traffic_type, event_type, value = nil)
190
+ @events_repository.add(key, traffic_type, event_type, (Time.now.to_f * 1000).to_i, value)
191
+ end
192
+
193
+ def keys_from_key(key)
194
+ case key.class.to_s
195
+ when 'Hash'
196
+ key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k.to_s }
197
+ else
198
+ [nil, key].map { |k| k.nil? ? nil : k.to_s }
199
+ end
200
+ end
201
+
202
+ def parsed_treatment(multiple, treatment_data)
203
+ if multiple
204
+ {
205
+ treatment: treatment_data[:treatment],
206
+ label: treatment_data[:label],
207
+ change_number: treatment_data[:change_number]
208
+ }
209
+ else
210
+ treatment_data[:treatment]
211
+ end
212
+ end
213
+ end
214
+ end