splitclient-rb 1.0.2.wip → 8.11.1.pre.rc1-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 +5 -5
- data/.github/CODEOWNERS +1 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/ci.yml +90 -0
- data/.github/workflows/update-license-year.yml +45 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +76 -0
- data/.simplecov +1 -0
- data/CHANGES.txt +361 -2
- data/CONTRIBUTORS-GUIDE.md +49 -0
- data/Gemfile +2 -0
- data/LICENSE +3 -36
- data/NOTICE.txt +5 -0
- data/README.md +59 -127
- data/Rakefile +27 -3
- data/ext/murmurhash/MurmurHash3.java +302 -0
- data/gemfiles/faraday_after_0.13.gemfile +7 -0
- data/gemfiles/faraday_before_0.13.gemfile +8 -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/cache/adapters/cache_adapter.rb +130 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
- data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
- data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
- data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
- data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
- data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
- data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
- data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
- data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +45 -0
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
- data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
- data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +56 -0
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
- data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
- data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
- data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
- data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
- data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
- data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
- data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +95 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
- data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
- data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
- data/lib/splitclient-rb/clients/split_client.rb +530 -0
- data/lib/splitclient-rb/constants.rb +16 -0
- data/lib/splitclient-rb/engine/api/client.rb +87 -0
- data/lib/splitclient-rb/engine/api/events.rb +57 -0
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
- data/lib/splitclient-rb/engine/api/segments.rb +78 -0
- data/lib/splitclient-rb/engine/api/splits.rb +139 -0
- data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
- data/lib/splitclient-rb/engine/auth_api_client.rb +100 -0
- data/lib/splitclient-rb/engine/back_off.rb +26 -0
- data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
- data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
- data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
- data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
- data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
- data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
- data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
- data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
- data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
- data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
- data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
- data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
- data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
- data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
- data/lib/splitclient-rb/engine/matchers/set_matcher.rb +27 -0
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
- data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
- data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
- data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
- data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
- data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
- data/lib/splitclient-rb/engine/models/label.rb +10 -0
- data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
- data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/split.rb +17 -0
- data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
- data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
- data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
- data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
- data/lib/splitclient-rb/engine/push_manager.rb +66 -0
- data/lib/splitclient-rb/engine/status_manager.rb +39 -0
- data/lib/splitclient-rb/engine/sync_manager.rb +183 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
- data/lib/splitclient-rb/exceptions.rb +26 -0
- data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
- data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
- data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
- data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
- data/lib/splitclient-rb/helpers/util.rb +26 -0
- data/lib/splitclient-rb/managers/split_manager.rb +121 -0
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_config.rb +591 -47
- data/lib/splitclient-rb/split_factory.rb +284 -0
- data/lib/splitclient-rb/split_factory_builder.rb +7 -0
- data/lib/splitclient-rb/split_factory_registry.rb +63 -0
- data/lib/splitclient-rb/split_logger.rb +23 -0
- data/lib/splitclient-rb/sse/event_source/client.rb +264 -0
- data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
- data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
- data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
- data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
- data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
- data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
- data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
- data/lib/splitclient-rb/sse/workers/splits_worker.rb +153 -0
- data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
- data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
- data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
- data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
- data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
- data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
- data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
- data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
- data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
- data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
- data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
- data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
- data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
- data/lib/splitclient-rb/utilitites.rb +49 -0
- data/lib/splitclient-rb/validators.rb +361 -0
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +177 -25
- data/sonar-project.properties +6 -0
- data/splitclient-rb.gemspec +53 -26
- data/tasks/benchmark_get_treatment.rake +31 -17
- data/tasks/irb.rake +6 -0
- metadata +411 -86
- data/NEWS +0 -9
- data/lib/splitclient-cache/local_store.rb +0 -45
- data/lib/splitclient-engine/impressions/impressions.rb +0 -79
- data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
- data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
- data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
- data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
- data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
- data/lib/splitclient-engine/metrics/metrics.rb +0 -158
- data/lib/splitclient-engine/parser/condition.rb +0 -143
- data/lib/splitclient-engine/parser/segment.rb +0 -84
- data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
- data/lib/splitclient-engine/parser/split.rb +0 -68
- data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
- data/lib/splitclient-engine/parser/split_parser.rb +0 -139
- data/lib/splitclient-engine/partitions/treatments.rb +0 -40
- data/lib/splitclient-rb/split_client.rb +0 -156
- data/lib/splitclient-rb_utilitites.rb +0 -26
- data/tasks/console.rake +0 -4
- data/tasks/rspec.rake +0 -3
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
require 'thread'
|
|
3
|
-
require 'faraday/http_cache'
|
|
4
|
-
require 'bundler/vendor/net/http/persistent'
|
|
5
|
-
require 'faraday_middleware'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
module SplitIoClient
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
# acts as an api adapater to connect to split endpoints
|
|
12
|
-
# uses a configuration object that can be modified when creating the client instance
|
|
13
|
-
# also, uses safe threads to execute fetches and post give the time execution values from the config
|
|
14
|
-
#
|
|
15
|
-
class SplitAdapter < NoMethodError
|
|
16
|
-
#
|
|
17
|
-
# handler for impressions
|
|
18
|
-
attr_reader :impressions
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
# handler for metrics
|
|
22
|
-
attr_reader :metrics
|
|
23
|
-
|
|
24
|
-
#
|
|
25
|
-
# handler for parsed splits
|
|
26
|
-
attr_reader :parsed_splits
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
# handeler for parsed segments
|
|
30
|
-
attr_reader :parsed_segments
|
|
31
|
-
|
|
32
|
-
attr_reader :impressions_producer
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
# Creates a new split api adapter instance that consumes split api endpoints
|
|
36
|
-
#
|
|
37
|
-
# @param api_key [String] the API key for your split account
|
|
38
|
-
#
|
|
39
|
-
# @return [SplitIoClient] split.io client instance
|
|
40
|
-
def initialize(api_key, config)
|
|
41
|
-
|
|
42
|
-
@api_key = api_key
|
|
43
|
-
@config = config
|
|
44
|
-
@parsed_splits = SplitParser.new(@config.logger)
|
|
45
|
-
@parsed_segments = SegmentParser.new(@config.logger)
|
|
46
|
-
@impressions = Impressions.new(100)
|
|
47
|
-
@metrics = Metrics.new(100)
|
|
48
|
-
|
|
49
|
-
@api_client = Faraday.new do |builder|
|
|
50
|
-
builder.use Faraday::HttpCache, store: @config.local_store
|
|
51
|
-
builder.use FaradayMiddleware::Gzip
|
|
52
|
-
builder.adapter :net_http_persistent
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
@splits_consumer = create_splits_api_consumer
|
|
56
|
-
@segments_consumer = create_segments_api_consumer
|
|
57
|
-
@metrics_producer = create_metrics_api_producer
|
|
58
|
-
@impressions_producer = create_impressions_api_producer
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
# creates a safe thread that will be executing api calls
|
|
63
|
-
# for fetching splits and segments give the execution time
|
|
64
|
-
# provided within the configuration
|
|
65
|
-
#
|
|
66
|
-
# @return [void]
|
|
67
|
-
def create_splits_api_consumer
|
|
68
|
-
Thread.new do
|
|
69
|
-
loop do
|
|
70
|
-
begin
|
|
71
|
-
#splits fetcher
|
|
72
|
-
splits_arr = []
|
|
73
|
-
data = get_splits(@parsed_splits.since)
|
|
74
|
-
data[:splits].each do |split|
|
|
75
|
-
splits_arr << SplitIoClient::Split.new(split)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
if @parsed_splits.is_empty?
|
|
79
|
-
@parsed_splits.splits = splits_arr
|
|
80
|
-
else
|
|
81
|
-
refresh_splits(splits_arr)
|
|
82
|
-
end
|
|
83
|
-
@parsed_splits.since = data[:till]
|
|
84
|
-
|
|
85
|
-
random_interval = randomize_interval @config.features_refresh_rate
|
|
86
|
-
sleep(random_interval)
|
|
87
|
-
rescue StandardError => error
|
|
88
|
-
@config.log_found_exception(__method__.to_s, error)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def create_segments_api_consumer
|
|
95
|
-
Thread.new do
|
|
96
|
-
loop do
|
|
97
|
-
begin
|
|
98
|
-
#segments fetcher
|
|
99
|
-
segments_arr = []
|
|
100
|
-
segment_data = get_segments(@parsed_splits.get_used_segments)
|
|
101
|
-
segment_data.each do |segment|
|
|
102
|
-
segments_arr << SplitIoClient::Segment.new(segment)
|
|
103
|
-
end
|
|
104
|
-
if @parsed_segments.is_empty?
|
|
105
|
-
@parsed_segments.segments = segments_arr
|
|
106
|
-
@parsed_segments.segments.map { |s| s.refresh_users(s.added, s.removed) }
|
|
107
|
-
else
|
|
108
|
-
refresh_segments(segments_arr)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
random_interval = randomize_interval @config.segments_refresh_rate
|
|
112
|
-
sleep(random_interval)
|
|
113
|
-
rescue StandardError => error
|
|
114
|
-
@config.log_found_exception(__method__.to_s, error)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
#
|
|
121
|
-
# helper method to execute a get request to the provided endpoint
|
|
122
|
-
#
|
|
123
|
-
# @param path [string] api endpoint path
|
|
124
|
-
# @param params [object] hash of params that will be added to the request
|
|
125
|
-
#
|
|
126
|
-
# @return [object] response to the request
|
|
127
|
-
def call_api(path, params = {})
|
|
128
|
-
@api_client.get @config.base_uri + path, params do |req|
|
|
129
|
-
req.headers['Authorization'] = 'Bearer ' + @api_key
|
|
130
|
-
req.headers['SplitSDKVersion'] = SplitIoClient::SplitClient.sdk_version
|
|
131
|
-
req.headers['SplitSDKMachineName'] = @config.machine_name
|
|
132
|
-
req.headers['SplitSDKMachineIP'] = @config.machine_ip
|
|
133
|
-
req.headers['Accept-Encoding'] = 'gzip'
|
|
134
|
-
req.options.open_timeout = @config.connection_timeout
|
|
135
|
-
req.options.timeout = @config.read_timeout
|
|
136
|
-
@config.logger.debug("GET #{@config.base_uri + path}") if @config.debug_enabled
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
# helper method to execute a post request to the provided endpoint
|
|
142
|
-
#
|
|
143
|
-
# @param path [string] api endpoint path
|
|
144
|
-
# @param params [object] hash of params that will be added to the request
|
|
145
|
-
#
|
|
146
|
-
# @return [object] response to the request
|
|
147
|
-
def post_api(path, param)
|
|
148
|
-
@api_client.post (@config.base_uri + path) do |req|
|
|
149
|
-
req.headers['Authorization'] = 'Bearer ' + @api_key
|
|
150
|
-
req.headers['Content-Type'] = 'application/json'
|
|
151
|
-
req.headers['SplitSDKVersion'] = SplitIoClient::SplitClient.sdk_version
|
|
152
|
-
req.headers['SplitSDKMachineName'] = @config.machine_name
|
|
153
|
-
req.headers['SplitSDKMachineIP'] = @config.machine_ip
|
|
154
|
-
req.body = param.to_json
|
|
155
|
-
req.options.timeout = @config.read_timeout
|
|
156
|
-
req.options.open_timeout = @config.connection_timeout
|
|
157
|
-
@config.logger.debug("POST #{@config.base_uri + path} #{req.body}") if @config.debug_enabled
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
#
|
|
162
|
-
# helper method to fetch splits by using the appropriate api endpoint
|
|
163
|
-
#
|
|
164
|
-
# @param since [int] since value for the last fetch
|
|
165
|
-
#
|
|
166
|
-
# @return splits [object] splits structure in json format
|
|
167
|
-
def get_splits(since)
|
|
168
|
-
result = nil
|
|
169
|
-
start = Time.now
|
|
170
|
-
prefix = 'splitChangeFetcher'
|
|
171
|
-
|
|
172
|
-
splits = call_api('/splitChanges', {:since => since})
|
|
173
|
-
|
|
174
|
-
if splits.status / 100 == 2
|
|
175
|
-
result = JSON.parse(splits.body, symbolize_names: true)
|
|
176
|
-
@metrics.count(prefix + '.status.' + splits.status.to_s, 1)
|
|
177
|
-
@config.logger.info("#{result[:splits].length} splits retrieved.")
|
|
178
|
-
@config.logger.debug("#{result}") if @config.debug_enabled
|
|
179
|
-
else
|
|
180
|
-
@config.logger.error('Unexpected result from API call')
|
|
181
|
-
@metrics.count(prefix + '.status.' + splits.status.to_s, 1)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
latency = (Time.now - start) * 1000.0
|
|
185
|
-
@metrics.time(prefix + '.time', latency)
|
|
186
|
-
|
|
187
|
-
result
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
#
|
|
191
|
-
# helper method to refresh splits values after a new fetch with changes
|
|
192
|
-
#
|
|
193
|
-
# @param splits_arr [object] array of splits to refresh
|
|
194
|
-
#
|
|
195
|
-
# @return [void]
|
|
196
|
-
def refresh_splits(splits_arr)
|
|
197
|
-
feature_names = splits_arr.map { |s| s.name }
|
|
198
|
-
@parsed_splits.splits.delete_if { |sp| feature_names.include?(sp.name) }
|
|
199
|
-
@parsed_splits.splits += splits_arr
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
#
|
|
203
|
-
# helper method to fetch segments by using the appropriate api endpoint
|
|
204
|
-
#
|
|
205
|
-
# @param names [object] array of segment names that must be fetched
|
|
206
|
-
#
|
|
207
|
-
# @return segments [object] segments structure in json format
|
|
208
|
-
def get_segments(names)
|
|
209
|
-
segments = []
|
|
210
|
-
start = Time.now
|
|
211
|
-
prefix = 'segmentChangeFetcher'
|
|
212
|
-
|
|
213
|
-
names.each do |name|
|
|
214
|
-
curr_segment = @parsed_segments.get_segment(name)
|
|
215
|
-
since = curr_segment.nil? ? -1 : curr_segment.till
|
|
216
|
-
|
|
217
|
-
while true
|
|
218
|
-
segment = call_api('/segmentChanges/' + name, {:since => since})
|
|
219
|
-
|
|
220
|
-
if segment.status / 100 == 2
|
|
221
|
-
segment_content = JSON.parse(segment.body, symbolize_names: true)
|
|
222
|
-
@parsed_segments.since = segment_content[:till]
|
|
223
|
-
@metrics.count(prefix + '.status.' + segment.status.to_s, 1)
|
|
224
|
-
@config.logger.info("\'#{segment_content[:name]}\' segment retrieved.")
|
|
225
|
-
@config.logger.debug("#{segment_content}") if @config.debug_enabled
|
|
226
|
-
segments << segment_content
|
|
227
|
-
else
|
|
228
|
-
@config.logger.error('Unexpected result from API call')
|
|
229
|
-
@metrics.count(prefix + '.status.' + segment.status.to_s, 1)
|
|
230
|
-
end
|
|
231
|
-
break if (since.to_i >= @parsed_segments.since.to_i)
|
|
232
|
-
since = @parsed_segments.since
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
latency = (Time.now - start) * 1000.0
|
|
237
|
-
@metrics.time(prefix + '.time', latency)
|
|
238
|
-
|
|
239
|
-
segments
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
#
|
|
243
|
-
# helper method to refresh segments values after a new fetch with changes
|
|
244
|
-
#
|
|
245
|
-
# @param segments_arr [object] array of segments to refresh
|
|
246
|
-
#
|
|
247
|
-
# @return [void]
|
|
248
|
-
def refresh_segments(segments_arr)
|
|
249
|
-
segment_names = @parsed_segments.segments.map { |s| s.name }
|
|
250
|
-
segments_arr.each do |s|
|
|
251
|
-
if segment_names.include?(s.name)
|
|
252
|
-
segment_to_update = @parsed_segments.get_segment(s.name)
|
|
253
|
-
segment_to_update.refresh_users(s.added, s.removed)
|
|
254
|
-
else
|
|
255
|
-
@parsed_segments.segments << s
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
#
|
|
261
|
-
# @return parsed_splits [object] parsed splits for this adapter
|
|
262
|
-
def parsed_splits
|
|
263
|
-
@parsed_splits
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
#
|
|
267
|
-
# @return parsed_segments [object] parsed segments for this adapter
|
|
268
|
-
def parsed_segments
|
|
269
|
-
@parsed_segments
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
#
|
|
273
|
-
# creates two safe threads that will be executing api calls
|
|
274
|
-
# for posting impressions and metrics given the execution time
|
|
275
|
-
# provided within the configuration
|
|
276
|
-
#
|
|
277
|
-
|
|
278
|
-
def create_metrics_api_producer
|
|
279
|
-
Thread.new do
|
|
280
|
-
loop do
|
|
281
|
-
begin
|
|
282
|
-
#post captured metrics
|
|
283
|
-
post_metrics
|
|
284
|
-
|
|
285
|
-
random_interval = randomize_interval @config.metrics_refresh_rate
|
|
286
|
-
sleep(random_interval)
|
|
287
|
-
rescue StandardError => error
|
|
288
|
-
@config.log_found_exception(__method__.to_s, error)
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def create_impressions_api_producer
|
|
295
|
-
Thread.new do
|
|
296
|
-
loop do
|
|
297
|
-
begin
|
|
298
|
-
#post captured impressions
|
|
299
|
-
post_impressions
|
|
300
|
-
|
|
301
|
-
random_interval = randomize_interval @config.impressions_refresh_rate
|
|
302
|
-
sleep(random_interval)
|
|
303
|
-
rescue StandardError => error
|
|
304
|
-
@config.log_found_exception(__method__.to_s, error)
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
end
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
#
|
|
311
|
-
# creates the appropriate json data for the cached impressions values
|
|
312
|
-
# and then sends them to the appropriate api endpoint with a valid body format
|
|
313
|
-
#
|
|
314
|
-
# @return [void]
|
|
315
|
-
def post_impressions
|
|
316
|
-
if @impressions.queue.empty?
|
|
317
|
-
@config.logger.info('No impressions to report.')
|
|
318
|
-
else
|
|
319
|
-
popped_impressions = @impressions.clear
|
|
320
|
-
test_impression_array = []
|
|
321
|
-
popped_impressions.each do |i|
|
|
322
|
-
filtered = []
|
|
323
|
-
keys_seen = []
|
|
324
|
-
|
|
325
|
-
impressions = i[:impressions]
|
|
326
|
-
impressions.each do |imp|
|
|
327
|
-
if keys_seen.include?(imp.key)
|
|
328
|
-
next
|
|
329
|
-
end
|
|
330
|
-
keys_seen << imp.key
|
|
331
|
-
filtered << imp
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
if filtered.empty?
|
|
335
|
-
@config.logger.info('No impressions to report post filtering.')
|
|
336
|
-
else
|
|
337
|
-
test_impression = {}
|
|
338
|
-
key_impressions = []
|
|
339
|
-
|
|
340
|
-
filtered.each do |f|
|
|
341
|
-
key_impressions << {keyName: f.key, treatment: f.treatment, time: f.time.to_i}
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
test_impression = {testName: i[:feature], keyImpressions: key_impressions}
|
|
345
|
-
test_impression_array << test_impression
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
res = post_api('/testImpressions/bulk', test_impression_array)
|
|
350
|
-
if res.status / 100 != 2
|
|
351
|
-
@config.logger.error("Unexpected status code while posting impressions: #{res.status}")
|
|
352
|
-
else
|
|
353
|
-
@config.logger.info("Impressions reported.")
|
|
354
|
-
@config.logger.debug("#{test_impression_array}")if @config.debug_enabled
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
#
|
|
360
|
-
# creates the appropriate json data for the cached metrics values
|
|
361
|
-
# include latencies, counts and gauges
|
|
362
|
-
# and then sends them to the appropriate api endpoint with a valida body format
|
|
363
|
-
#
|
|
364
|
-
# @return [void]
|
|
365
|
-
def post_metrics
|
|
366
|
-
clear = true
|
|
367
|
-
if @metrics.latencies.empty?
|
|
368
|
-
@config.logger.info('No latencies to report.')
|
|
369
|
-
else
|
|
370
|
-
@metrics.latencies.each do |l|
|
|
371
|
-
metrics_time = {}
|
|
372
|
-
metrics_time = {name: l[:operation], latencies: l[:latencies]}
|
|
373
|
-
res = post_api('/metrics/time', metrics_time)
|
|
374
|
-
if res.status / 100 != 2
|
|
375
|
-
@config.logger.error("Unexpected status code while posting time metrics: #{res.status}")
|
|
376
|
-
clear = false
|
|
377
|
-
else
|
|
378
|
-
@config.logger.info("Metric time reported.")
|
|
379
|
-
@config.logger.debug("#{metrics_time}") if @config.debug_enabled
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
@metrics.latencies.clear if clear
|
|
384
|
-
|
|
385
|
-
clear = true
|
|
386
|
-
if @metrics.counts.empty?
|
|
387
|
-
@config.logger.info('No counts to report.')
|
|
388
|
-
else
|
|
389
|
-
@metrics.counts.each do |c|
|
|
390
|
-
metrics_count = {}
|
|
391
|
-
metrics_count = {name: c[:name], delta: c[:delta].sum}
|
|
392
|
-
res = post_api('/metrics/counter', metrics_count)
|
|
393
|
-
if res.status / 100 != 2
|
|
394
|
-
@config.logger.error("Unexpected status code while posting count metrics: #{res.status}")
|
|
395
|
-
clear = false
|
|
396
|
-
else
|
|
397
|
-
@config.logger.info("Metric counts reported.")
|
|
398
|
-
@config.logger.debug("#{metrics_count}") if @config.debug_enabled
|
|
399
|
-
end
|
|
400
|
-
end
|
|
401
|
-
end
|
|
402
|
-
@metrics.counts.clear if clear
|
|
403
|
-
|
|
404
|
-
clear = true
|
|
405
|
-
if @metrics.gauges.empty?
|
|
406
|
-
@config.logger.info('No gauges to report.')
|
|
407
|
-
else
|
|
408
|
-
@metrics.gauges.each do |g|
|
|
409
|
-
metrics_gauge = {}
|
|
410
|
-
metrics_gauge = {name: g[:name], value: g[:gauge].value}
|
|
411
|
-
res = post_api('/metrics/gauge', metrics_gauge)
|
|
412
|
-
if res.status / 100 != 2
|
|
413
|
-
@config.logger.error("Unexpected status code while posting gauge metrics: #{res.status}")
|
|
414
|
-
clear = false
|
|
415
|
-
else
|
|
416
|
-
@config.logger.info("Metric gauge reported.")
|
|
417
|
-
@config.logger.debug("#{metrics_gauge}") if @config.debug_enabled
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
end
|
|
421
|
-
@metrics.gauges.clear if clear
|
|
422
|
-
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
private
|
|
426
|
-
|
|
427
|
-
def randomize_interval(interval)
|
|
428
|
-
@random_generator ||= Random.new
|
|
429
|
-
random_factor = @random_generator.rand(50..100)/100.0
|
|
430
|
-
interval * random_factor
|
|
431
|
-
end
|
|
432
|
-
end
|
|
433
|
-
end
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
module SplitIoClient
|
|
2
|
-
#
|
|
3
|
-
# helper class to parse fetched splits
|
|
4
|
-
#
|
|
5
|
-
class SplitParser < NoMethodError
|
|
6
|
-
#
|
|
7
|
-
# since value for splitChanges last fetch
|
|
8
|
-
attr_accessor :since
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
# till value for splitChanges last fetch
|
|
12
|
-
attr_accessor :till
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
# splits data
|
|
16
|
-
attr_accessor :splits
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
# splits segments data
|
|
20
|
-
attr_accessor :segments
|
|
21
|
-
|
|
22
|
-
def initialize(logger)
|
|
23
|
-
@splits = []
|
|
24
|
-
@since = -1
|
|
25
|
-
@till = -1
|
|
26
|
-
@logger = logger
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
# gets all the split names retrived from the endpoint
|
|
31
|
-
#
|
|
32
|
-
# @return [object] array of split names
|
|
33
|
-
def get_split_names
|
|
34
|
-
@splits.map { |s| s.name }
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
# @return [boolean] true if the splits content is empty false otherwise
|
|
39
|
-
def is_empty?
|
|
40
|
-
@splits.empty? ? true : false
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
#
|
|
44
|
-
# gets all the segment names that are used within the retrieved splits
|
|
45
|
-
#
|
|
46
|
-
# @return [object] array of segment names
|
|
47
|
-
def get_used_segments
|
|
48
|
-
segment_names = []
|
|
49
|
-
|
|
50
|
-
@splits.each { |s|
|
|
51
|
-
s.conditions.each { |c|
|
|
52
|
-
c.matchers.each { |m|
|
|
53
|
-
m[:userDefinedSegmentMatcherData].each { |seg, name|
|
|
54
|
-
segment_names << name
|
|
55
|
-
} unless m[:userDefinedSegmentMatcherData].nil?
|
|
56
|
-
} unless c.matchers.nil?
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
segment_names.uniq
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
#
|
|
63
|
-
# gets a split parsed object by name
|
|
64
|
-
#
|
|
65
|
-
# @param name [string] name of the split
|
|
66
|
-
#
|
|
67
|
-
# @return split [object] split object
|
|
68
|
-
def get_split(name)
|
|
69
|
-
@splits.find { |s| s.name == name }
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
#
|
|
73
|
-
# gets the treatment for the given combination of user key and split name
|
|
74
|
-
# using all parsed data for the splits
|
|
75
|
-
#
|
|
76
|
-
# @param id [string] user key
|
|
77
|
-
# @param name [string] split name
|
|
78
|
-
# @param default_treatment [string] default treatment value to be returned
|
|
79
|
-
#
|
|
80
|
-
# @return treatment [object] treatment for this user key, split pair
|
|
81
|
-
def get_split_treatment(id, name, default_treatment, attributes = nil)
|
|
82
|
-
split = get_split(name)
|
|
83
|
-
attribute_matchers = ["ATTR_WHITELIST", "EQUAL_TO", "GREATER_THAN_OR_EQUAL_TO", "LESS_THAN_OR_EQUAL_TO", "BETWEEN"]
|
|
84
|
-
|
|
85
|
-
if !split.is_empty? && split.status == 'ACTIVE' && !split.killed?
|
|
86
|
-
split.conditions.each do |c|
|
|
87
|
-
unless c.is_empty?
|
|
88
|
-
matcher = get_matcher_type(c)
|
|
89
|
-
matches = attribute_matchers.include?(matcher.matcher_type) ? matcher.match?(attributes) : matcher.match?(id)
|
|
90
|
-
if matches
|
|
91
|
-
result = Splitter.get_treatment(id, split.seed, c.partitions) #'true match - running split'
|
|
92
|
-
if result.nil?
|
|
93
|
-
return default_treatment
|
|
94
|
-
else
|
|
95
|
-
return result
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
default_treatment
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
#
|
|
106
|
-
# gets the matcher type from a condition object
|
|
107
|
-
#
|
|
108
|
-
# @param contidion [object] a condition object
|
|
109
|
-
#
|
|
110
|
-
# @return matcher [object] the matcher object for the given condition
|
|
111
|
-
def get_matcher_type(condition)
|
|
112
|
-
final_matcher = nil
|
|
113
|
-
|
|
114
|
-
case condition.matcher
|
|
115
|
-
when 'ALL_KEYS'
|
|
116
|
-
final_matcher = AllKeysMatcher.new
|
|
117
|
-
when 'IN_SEGMENT'
|
|
118
|
-
segment = @segments.get_segment(condition.matcher_segment)
|
|
119
|
-
final_matcher = segment.is_empty? ? UserDefinedSegmentMatcher.new(nil) : UserDefinedSegmentMatcher.new(segment)
|
|
120
|
-
when 'WHITELIST'
|
|
121
|
-
final_matcher = WhitelistMatcher.new(condition.matcher_whitelist)
|
|
122
|
-
when 'EQUAL_TO'
|
|
123
|
-
final_matcher = EqualToMatcher.new(condition.matcher_equal)
|
|
124
|
-
when 'GREATER_THAN_OR_EQUAL_TO'
|
|
125
|
-
final_matcher = GreaterThanOrEqualToMatcher.new(condition.matcher_greater_than_or_equal)
|
|
126
|
-
when 'LESS_THAN_OR_EQUAL_TO'
|
|
127
|
-
final_matcher = LessThanOrEqualToMatcher.new(condition.matcher_less_than_or_equal)
|
|
128
|
-
when 'BETWEEN'
|
|
129
|
-
final_matcher = BetweenMatcher.new(condition.matcher_between)
|
|
130
|
-
else
|
|
131
|
-
@logger.error('Invalid matcher type')
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
final_matcher
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module SplitIoClient
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# represents the possible return values for a treatment
|
|
5
|
-
#
|
|
6
|
-
class Treatments < NoMethodError
|
|
7
|
-
|
|
8
|
-
# Constants to represent treatment values
|
|
9
|
-
CONTROL = 'control'
|
|
10
|
-
OFF = 'off'
|
|
11
|
-
ON = 'on'
|
|
12
|
-
|
|
13
|
-
# get the actual value for the given treatment type
|
|
14
|
-
#
|
|
15
|
-
# @param type [string] treatment type
|
|
16
|
-
#
|
|
17
|
-
# @return [Treatment] treatment type value
|
|
18
|
-
def self.get_type(type)
|
|
19
|
-
case type
|
|
20
|
-
when 'on'
|
|
21
|
-
return ON
|
|
22
|
-
when 'off', 'control'
|
|
23
|
-
return CONTROL
|
|
24
|
-
else # default return off
|
|
25
|
-
return CONTROL
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# checks if the give treatment matches control type
|
|
30
|
-
#
|
|
31
|
-
# @param type [string] treatment type
|
|
32
|
-
#
|
|
33
|
-
# @return [boolean] true if matches, false otherwise
|
|
34
|
-
def self.is_control?(treatment)
|
|
35
|
-
get_type(treatment).equal?(CONTROL) ? true : false
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
end
|