splitclient-rb 6.3.0 → 8.11.0
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 +4 -4
- 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 +4 -0
- data/.rubocop.yml +46 -3
- data/CHANGES.txt +158 -11
- data/CONTRIBUTORS-GUIDE.md +49 -0
- data/LICENSE +169 -13
- data/NOTICE.txt +5 -0
- data/README.md +67 -27
- data/Rakefile +1 -8
- data/ext/murmurhash/3_x64_128.c +117 -0
- data/ext/murmurhash/murmurhash.c +5 -1
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +3 -3
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +4 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +7 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +12 -4
- 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 +26 -14
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +9 -14
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +31 -10
- 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 +22 -23
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +15 -22
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +6 -31
- data/lib/splitclient-rb/cache/repositories/repository.rb +6 -5
- data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +46 -6
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +232 -43
- data/lib/splitclient-rb/cache/routers/impression_router.rb +24 -22
- data/lib/splitclient-rb/cache/senders/events_sender.rb +12 -29
- 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 +27 -13
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +11 -25
- 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 +385 -138
- data/lib/splitclient-rb/constants.rb +16 -0
- data/lib/splitclient-rb/engine/api/client.rb +38 -43
- data/lib/splitclient-rb/engine/api/events.rb +19 -11
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +1 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +49 -14
- data/lib/splitclient-rb/engine/api/segments.rb +31 -24
- data/lib/splitclient-rb/engine/api/splits.rb +108 -33
- data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
- data/lib/splitclient-rb/engine/auth_api_client.rb +96 -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-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-rb/engine/matchers/all_keys_matcher.rb +1 -1
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +10 -8
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +2 -6
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +1 -5
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +6 -5
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +5 -4
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +6 -4
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +1 -5
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +6 -4
- 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 +6 -4
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +22 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +2 -6
- 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 +2 -1
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +4 -3
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +3 -65
- 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 +3 -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 +14 -0
- data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
- data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +81 -20
- data/lib/splitclient-rb/engine/parser/evaluator.rb +40 -51
- 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 +180 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
- data/lib/splitclient-rb/exceptions.rb +20 -1
- 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 +58 -20
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_config.rb +336 -54
- data/lib/splitclient-rb/split_factory.rb +219 -33
- data/lib/splitclient-rb/split_factory_builder.rb +1 -22
- data/lib/splitclient-rb/split_factory_registry.rb +63 -0
- data/lib/splitclient-rb/split_logger.rb +9 -10
- data/lib/splitclient-rb/sse/event_source/client.rb +263 -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 +149 -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 +8 -0
- data/lib/splitclient-rb/validators.rb +142 -38
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +101 -16
- data/sonar-project.properties +6 -0
- data/splitclient-rb.gemspec +28 -23
- metadata +262 -82
- data/.travis.yml +0 -11
- data/Appraisals +0 -10
- data/Detailed-README.md +0 -588
- data/NEWS +0 -141
- data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +0 -127
- data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +0 -96
- data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +0 -21
- data/lib/splitclient-rb/cache/senders/metrics_sender.rb +0 -56
- data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +0 -46
- data/lib/splitclient-rb/cache/stores/segment_store.rb +0 -81
- data/lib/splitclient-rb/cache/stores/split_store.rb +0 -102
- data/lib/splitclient-rb/clients/localhost_split_client.rb +0 -183
- data/lib/splitclient-rb/engine/api/faraday_adapter/patched_net_http_persistent.rb +0 -46
- data/lib/splitclient-rb/engine/api/metrics.rb +0 -60
- data/lib/splitclient-rb/engine/metrics/metrics.rb +0 -80
- data/lib/splitclient-rb/engine/parser/split_adapter.rb +0 -81
- data/lib/splitclient-rb/localhost_split_factory.rb +0 -13
- data/lib/splitclient-rb/localhost_utils.rb +0 -59
- data/lib/splitclient-rb/managers/localhost_split_manager.rb +0 -60
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
class PushManager
|
|
6
|
+
def initialize(config, sse_handler, api_key, telemetry_runtime_producer)
|
|
7
|
+
@config = config
|
|
8
|
+
@sse_handler = sse_handler
|
|
9
|
+
@auth_api_client = AuthApiClient.new(@config, telemetry_runtime_producer)
|
|
10
|
+
@api_key = api_key
|
|
11
|
+
@back_off = Engine::BackOff.new(@config.auth_retry_back_off_base, 1)
|
|
12
|
+
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def start_sse
|
|
16
|
+
response = @auth_api_client.authenticate(@api_key)
|
|
17
|
+
@config.logger.debug("Auth service response push_enabled: #{response[:push_enabled]}") if @config.debug_enabled
|
|
18
|
+
|
|
19
|
+
if response[:push_enabled] && @sse_handler.start(response[:token], response[:channels])
|
|
20
|
+
schedule_next_token_refresh(response[:exp])
|
|
21
|
+
@back_off.reset
|
|
22
|
+
record_telemetry(response[:exp])
|
|
23
|
+
|
|
24
|
+
return true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
stop_sse
|
|
28
|
+
schedule_next_token_refresh(@back_off.interval) if response[:retry]
|
|
29
|
+
false
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
@config.logger.error("start_sse: #{e.inspect}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stop_sse
|
|
35
|
+
@sse_handler.stop
|
|
36
|
+
SplitIoClient::Helpers::ThreadHelper.stop(:schedule_next_token_refresh, @config)
|
|
37
|
+
rescue StandardError => e
|
|
38
|
+
@config.logger.error(e.inspect)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def schedule_next_token_refresh(time)
|
|
44
|
+
@config.threads[:schedule_next_token_refresh] = Thread.new { refresh_token_task(time) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def refresh_token_task(time)
|
|
48
|
+
@config.logger.debug("schedule_next_token_refresh refresh in #{time} seconds.") if @config.debug_enabled
|
|
49
|
+
|
|
50
|
+
sleep(time)
|
|
51
|
+
|
|
52
|
+
@config.logger.debug('schedule_next_token_refresh starting ...') if @config.debug_enabled
|
|
53
|
+
@sse_handler.stop
|
|
54
|
+
|
|
55
|
+
start_sse
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
@config.logger.debug("schedule_next_token_refresh error: #{e.inspect}") if @config.debug_enabled
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def record_telemetry(time)
|
|
61
|
+
data = (Time.now.to_f * 1000.0).to_i + (time * 1000.0).to_i
|
|
62
|
+
@telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::TOKEN_REFRESH, data)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
class StatusManager
|
|
6
|
+
def initialize(config, internal_events_queue)
|
|
7
|
+
@config = config
|
|
8
|
+
@sdk_ready = Concurrent::CountDownLatch.new(1)
|
|
9
|
+
@internal_events_queue = internal_events_queue
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def ready?
|
|
13
|
+
return true if @config.consumer?
|
|
14
|
+
|
|
15
|
+
@sdk_ready.wait(0)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ready!
|
|
19
|
+
return if ready?
|
|
20
|
+
|
|
21
|
+
@sdk_ready.count_down
|
|
22
|
+
@config.logger.info('SplitIO SDK is ready')
|
|
23
|
+
@internal_events_queue.push(
|
|
24
|
+
SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
|
|
25
|
+
SplitIoClient::Engine::Models::SdkInternalEvent::SDK_READY, nil
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def wait_until_ready(seconds = nil)
|
|
31
|
+
return if @config.consumer?
|
|
32
|
+
|
|
33
|
+
timeout = seconds || @config.block_until_ready
|
|
34
|
+
|
|
35
|
+
raise SDKBlockerTimeoutExpiredException, 'SDK start up timeout expired' unless @sdk_ready.wait(timeout)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
class SyncManager
|
|
6
|
+
SYNC_MODE_STREAMING = 0
|
|
7
|
+
SYNC_MODE_POLLING = 1
|
|
8
|
+
|
|
9
|
+
def initialize(config,
|
|
10
|
+
synchronizer,
|
|
11
|
+
telemetry_runtime_producer,
|
|
12
|
+
telemetry_synchronizer,
|
|
13
|
+
status_manager,
|
|
14
|
+
sse_handler,
|
|
15
|
+
push_manager,
|
|
16
|
+
status_queue)
|
|
17
|
+
@config = config
|
|
18
|
+
@synchronizer = synchronizer
|
|
19
|
+
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
20
|
+
@telemetry_synchronizer = telemetry_synchronizer
|
|
21
|
+
@status_manager = status_manager
|
|
22
|
+
@sse_handler = sse_handler
|
|
23
|
+
@push_manager = push_manager
|
|
24
|
+
@status_queue = status_queue
|
|
25
|
+
@sse_connected = Concurrent::AtomicBoolean.new(false)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def start
|
|
29
|
+
start_thread
|
|
30
|
+
PhusionPassenger.on_event(:starting_worker_process) { |forked| start_thread if forked } if defined?(PhusionPassenger)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def start_consumer
|
|
34
|
+
start_consumer_thread
|
|
35
|
+
PhusionPassenger.on_event(:starting_worker_process) { |forked| start_consumer_thread if forked } if defined?(PhusionPassenger)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def start_thread
|
|
41
|
+
@config.threads[:start_sdk] = Thread.new do
|
|
42
|
+
sleep(0.5) until @synchronizer.sync_all(false)
|
|
43
|
+
|
|
44
|
+
@status_manager.ready!
|
|
45
|
+
@telemetry_synchronizer.synchronize_config
|
|
46
|
+
@synchronizer.start_periodic_data_recording
|
|
47
|
+
connected = false
|
|
48
|
+
|
|
49
|
+
if @config.streaming_enabled
|
|
50
|
+
@config.logger.debug('Starting Streaming mode ...') if @config.debug_enabled
|
|
51
|
+
start_push_status_monitor
|
|
52
|
+
connected = @push_manager.start_sse
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unless connected
|
|
56
|
+
@config.logger.debug('Starting Polling mode ...') if @config.debug_enabled
|
|
57
|
+
@synchronizer.start_periodic_fetch
|
|
58
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def start_consumer_thread
|
|
64
|
+
@config.threads[:start_sdk_consumer] = Thread.new do
|
|
65
|
+
@status_manager.ready!
|
|
66
|
+
@telemetry_synchronizer.synchronize_config
|
|
67
|
+
@synchronizer.start_periodic_data_recording
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def process_subsystem_ready
|
|
72
|
+
@synchronizer.stop_periodic_fetch
|
|
73
|
+
@synchronizer.sync_all
|
|
74
|
+
@sse_handler.start_workers
|
|
75
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_STREAMING)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def process_subsystem_down
|
|
79
|
+
@sse_handler.stop_workers
|
|
80
|
+
@synchronizer.start_periodic_fetch
|
|
81
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def process_push_shutdown
|
|
85
|
+
@push_manager.stop_sse
|
|
86
|
+
@sse_handler.stop_workers
|
|
87
|
+
@synchronizer.start_periodic_fetch
|
|
88
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
89
|
+
rescue StandardError => e
|
|
90
|
+
@config.logger.error("process_push_shutdown error: #{e.inspect}")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def process_connected
|
|
94
|
+
if @sse_connected.value
|
|
95
|
+
@config.logger.debug('Streaming already connected.') if @config.debug_enabled
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
@sse_connected.make_true
|
|
100
|
+
@synchronizer.stop_periodic_fetch
|
|
101
|
+
@synchronizer.sync_all
|
|
102
|
+
@sse_handler.start_workers
|
|
103
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_STREAMING)
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
@config.logger.error("process_connected error: #{e.inspect}")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def process_forced_stop
|
|
109
|
+
unless @sse_connected.value
|
|
110
|
+
@config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
@sse_connected.make_false
|
|
115
|
+
@synchronizer.start_periodic_fetch
|
|
116
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
117
|
+
rescue StandardError => e
|
|
118
|
+
@config.logger.error("process_connected error: #{e.inspect}")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def process_disconnect(reconnect)
|
|
122
|
+
unless @sse_connected.value
|
|
123
|
+
@config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
|
|
124
|
+
return
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
@sse_connected.make_false
|
|
128
|
+
@sse_handler.stop_workers
|
|
129
|
+
@synchronizer.start_periodic_fetch
|
|
130
|
+
record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
|
|
131
|
+
|
|
132
|
+
if reconnect
|
|
133
|
+
@push_manager.stop_sse
|
|
134
|
+
@synchronizer.sync_all
|
|
135
|
+
@push_manager.start_sse
|
|
136
|
+
end
|
|
137
|
+
rescue StandardError => e
|
|
138
|
+
@config.logger.error("process_disconnect error: #{e.inspect}")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def record_telemetry(type, data)
|
|
142
|
+
@telemetry_runtime_producer.record_streaming_event(type, data)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def start_push_status_monitor
|
|
146
|
+
@config.threads[:push_status_handler] = Thread.new do
|
|
147
|
+
@config.logger.debug('Starting push status handler ...') if @config.debug_enabled
|
|
148
|
+
incoming_push_status_handler
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def incoming_push_status_handler
|
|
153
|
+
while (status = @status_queue.pop)
|
|
154
|
+
@config.logger.debug("Push status handler dequeue #{status}") if @config.debug_enabled
|
|
155
|
+
|
|
156
|
+
case status
|
|
157
|
+
when Constants::PUSH_CONNECTED
|
|
158
|
+
process_connected
|
|
159
|
+
when Constants::PUSH_RETRYABLE_ERROR
|
|
160
|
+
process_disconnect(true)
|
|
161
|
+
when Constants::PUSH_FORCED_STOP
|
|
162
|
+
process_forced_stop
|
|
163
|
+
when Constants::PUSH_NONRETRYABLE_ERROR
|
|
164
|
+
process_disconnect(false)
|
|
165
|
+
when Constants::PUSH_SUBSYSTEM_DOWN
|
|
166
|
+
process_subsystem_down
|
|
167
|
+
when Constants::PUSH_SUBSYSTEM_READY
|
|
168
|
+
process_subsystem_ready
|
|
169
|
+
when Constants::PUSH_SUBSYSTEM_OFF
|
|
170
|
+
process_push_shutdown
|
|
171
|
+
else
|
|
172
|
+
@config.logger.debug('Incorrect push status type.') if @config.debug_enabled
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
@config.logger.error("Push status handler error: #{e.inspect}")
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
class Synchronizer
|
|
6
|
+
include SplitIoClient::Cache::Fetchers
|
|
7
|
+
include SplitIoClient::Cache::Senders
|
|
8
|
+
|
|
9
|
+
ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES = 10
|
|
10
|
+
|
|
11
|
+
def initialize(
|
|
12
|
+
repositories,
|
|
13
|
+
config,
|
|
14
|
+
params
|
|
15
|
+
)
|
|
16
|
+
@splits_repository = repositories[:splits]
|
|
17
|
+
@segments_repository = repositories[:segments]
|
|
18
|
+
@rule_based_segments_repository = repositories[:rule_based_segments]
|
|
19
|
+
@impressions_repository = repositories[:impressions]
|
|
20
|
+
@events_repository = repositories[:events]
|
|
21
|
+
@config = config
|
|
22
|
+
@split_fetcher = params[:split_fetcher]
|
|
23
|
+
@segment_fetcher = params[:segment_fetcher]
|
|
24
|
+
@impressions_api = params[:impressions_api]
|
|
25
|
+
@impression_counter = params[:imp_counter]
|
|
26
|
+
@telemetry_synchronizer = params[:telemetry_synchronizer]
|
|
27
|
+
@impressions_sender_adapter = params[:impressions_sender_adapter]
|
|
28
|
+
@unique_keys_tracker = params[:unique_keys_tracker]
|
|
29
|
+
|
|
30
|
+
@splits_sync_backoff = Engine::BackOff.new(10, 0, 60)
|
|
31
|
+
@segments_sync_backoff = Engine::BackOff.new(10, 0, 60)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sync_all(asynchronous = true)
|
|
35
|
+
unless asynchronous
|
|
36
|
+
return sync_splits_and_segments
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@config.threads[:sync_all_thread] = Thread.new do
|
|
40
|
+
sync_splits_and_segments
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def start_periodic_data_recording
|
|
47
|
+
unless @config.consumer?
|
|
48
|
+
impressions_sender
|
|
49
|
+
events_sender
|
|
50
|
+
start_telemetry_sync_task
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
impressions_count_sender
|
|
54
|
+
start_unique_keys_tracker_task
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def start_periodic_fetch
|
|
58
|
+
@split_fetcher.call
|
|
59
|
+
@segment_fetcher.call
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def stop_periodic_fetch
|
|
63
|
+
@split_fetcher.stop_splits_thread
|
|
64
|
+
@segment_fetcher.stop_segments_thread
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def fetch_splits(target_change_number, rbs_target_change_number)
|
|
68
|
+
return if check_exit_conditions(target_change_number, rbs_target_change_number)
|
|
69
|
+
|
|
70
|
+
fetch_options = { cache_control_headers: true, till: nil }
|
|
71
|
+
|
|
72
|
+
result = attempt_splits_sync(target_change_number, rbs_target_change_number,
|
|
73
|
+
fetch_options,
|
|
74
|
+
@config.on_demand_fetch_max_retries,
|
|
75
|
+
@config.on_demand_fetch_retry_delay_seconds,
|
|
76
|
+
false)
|
|
77
|
+
|
|
78
|
+
attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
|
|
79
|
+
if result[:success]
|
|
80
|
+
@segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
|
|
81
|
+
@config.logger.debug("Refresh completed in #{attempts} attempts.") if @config.debug_enabled
|
|
82
|
+
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if target_change_number != 0
|
|
87
|
+
fetch_options[:till] = target_change_number
|
|
88
|
+
else
|
|
89
|
+
fetch_options[:till] = rbs_target_change_number
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
result = attempt_splits_sync(target_change_number, rbs_target_change_number,
|
|
93
|
+
fetch_options,
|
|
94
|
+
ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES,
|
|
95
|
+
nil,
|
|
96
|
+
true)
|
|
97
|
+
|
|
98
|
+
attempts = ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES - result[:remaining_attempts]
|
|
99
|
+
|
|
100
|
+
process_result(result, attempts)
|
|
101
|
+
rescue StandardError => e
|
|
102
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def fetch_segment(name, target_change_number)
|
|
106
|
+
return if target_change_number <= @segments_repository.get_change_number(name).to_i
|
|
107
|
+
|
|
108
|
+
fetch_options = { cache_control_headers: true, till: nil }
|
|
109
|
+
result = attempt_segment_sync(name,
|
|
110
|
+
target_change_number,
|
|
111
|
+
fetch_options,
|
|
112
|
+
@config.on_demand_fetch_max_retries,
|
|
113
|
+
@config.on_demand_fetch_retry_delay_seconds,
|
|
114
|
+
false)
|
|
115
|
+
|
|
116
|
+
attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
|
|
117
|
+
if result[:success]
|
|
118
|
+
@config.logger.debug("Segment #{name} refresh completed in #{attempts} attempts.") if @config.debug_enabled
|
|
119
|
+
|
|
120
|
+
return
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
fetch_options = { cache_control_headers: true, till: target_change_number }
|
|
124
|
+
result = attempt_segment_sync(name,
|
|
125
|
+
target_change_number,
|
|
126
|
+
fetch_options,
|
|
127
|
+
ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES,
|
|
128
|
+
nil,
|
|
129
|
+
true)
|
|
130
|
+
|
|
131
|
+
attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
|
|
132
|
+
if result[:success]
|
|
133
|
+
@config.logger.debug("Segment #{name} refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
|
|
134
|
+
else
|
|
135
|
+
@config.logger.debug("No changes fetched for segment #{name} after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
|
|
136
|
+
end
|
|
137
|
+
rescue StandardError => e
|
|
138
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def process_result(result, attempts)
|
|
144
|
+
if result[:success]
|
|
145
|
+
@segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
|
|
146
|
+
@config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
|
|
147
|
+
else
|
|
148
|
+
@config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def attempt_segment_sync(name, target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
|
|
153
|
+
remaining_attempts = max_retries
|
|
154
|
+
@segments_sync_backoff.reset
|
|
155
|
+
|
|
156
|
+
loop do
|
|
157
|
+
remaining_attempts -= 1
|
|
158
|
+
|
|
159
|
+
@segment_fetcher.fetch_segment(name, fetch_options)
|
|
160
|
+
|
|
161
|
+
return sync_result(true, remaining_attempts) if target_cn <= @segments_repository.get_change_number(name).to_i
|
|
162
|
+
return sync_result(false, remaining_attempts) if remaining_attempts <= 0
|
|
163
|
+
|
|
164
|
+
delay = with_backoff ? @segments_sync_backoff.interval : retry_delay_seconds
|
|
165
|
+
sleep(delay)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def attempt_splits_sync(target_cn, rbs_target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
|
|
170
|
+
remaining_attempts = max_retries
|
|
171
|
+
@splits_sync_backoff.reset
|
|
172
|
+
|
|
173
|
+
loop do
|
|
174
|
+
remaining_attempts -= 1
|
|
175
|
+
|
|
176
|
+
result = @split_fetcher.fetch_splits(fetch_options)
|
|
177
|
+
|
|
178
|
+
return sync_result(true, remaining_attempts, result[:segment_names]) if check_exit_conditions(target_cn, rbs_target_cn)
|
|
179
|
+
return sync_result(false, remaining_attempts, result[:segment_names]) if remaining_attempts <= 0
|
|
180
|
+
|
|
181
|
+
delay = with_backoff ? @splits_sync_backoff.interval : retry_delay_seconds
|
|
182
|
+
sleep(delay)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Starts thread which loops constantly and sends impressions to the Split API
|
|
187
|
+
def impressions_sender
|
|
188
|
+
ImpressionsSender.new(@impressions_repository, @config, @impressions_api).call
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Starts thread which loops constantly and sends events to the Split API
|
|
192
|
+
def events_sender
|
|
193
|
+
EventsSender.new(@events_repository, @config).call
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Starts thread which loops constantly and sends impressions count to the Split API
|
|
197
|
+
def impressions_count_sender
|
|
198
|
+
ImpressionsCountSender.new(@config, @impression_counter, @impressions_sender_adapter).call
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def start_telemetry_sync_task
|
|
202
|
+
Telemetry::SyncTask.new(@config, @telemetry_synchronizer).call
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def start_unique_keys_tracker_task
|
|
206
|
+
@unique_keys_tracker.call
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def sync_result(success, remaining_attempts, segment_names = nil)
|
|
210
|
+
{ success: success, remaining_attempts: remaining_attempts, segment_names: segment_names }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def sync_splits_and_segments
|
|
214
|
+
@config.logger.debug('Synchronizing feature flags and segments ...') if @config.debug_enabled
|
|
215
|
+
splits_result = @split_fetcher.fetch_splits
|
|
216
|
+
|
|
217
|
+
splits_result[:success] && @segment_fetcher.fetch_segments
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def check_exit_conditions(target_change_number, rbs_target_change_number)
|
|
221
|
+
return true if rbs_target_change_number == 0 and target_change_number == 0
|
|
222
|
+
|
|
223
|
+
return target_change_number <= @splits_repository.get_change_number.to_i if rbs_target_change_number == 0
|
|
224
|
+
|
|
225
|
+
return rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i if target_change_number == 0
|
|
226
|
+
|
|
227
|
+
return (target_change_number <= @splits_repository.get_change_number.to_i and rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
module SplitIoClient
|
|
2
2
|
class SplitIoError < StandardError; end
|
|
3
3
|
|
|
4
|
-
class SDKShutdownException <
|
|
4
|
+
class SDKShutdownException < Exception; end
|
|
5
5
|
|
|
6
6
|
class SDKBlockerTimeoutExpiredException < SplitIoError; end
|
|
7
|
+
|
|
8
|
+
class SSEClientException < SplitIoError
|
|
9
|
+
attr_reader :event
|
|
10
|
+
|
|
11
|
+
def initialize(event)
|
|
12
|
+
@event = event
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ApiException < SplitIoError
|
|
17
|
+
def initialize(msg, exception_code)
|
|
18
|
+
@@exception_code = exception_code
|
|
19
|
+
super(msg)
|
|
20
|
+
end
|
|
21
|
+
def exception_code
|
|
22
|
+
@@exception_code
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
7
26
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
NO_COMPRESSION = 0
|
|
5
|
+
GZIP_COMPRESSION = 1
|
|
6
|
+
ZLIB_COMPRESSION = 2
|
|
7
|
+
|
|
8
|
+
module Helpers
|
|
9
|
+
class DecryptionHelper
|
|
10
|
+
def self.get_encoded_definition(compression, data)
|
|
11
|
+
case compression
|
|
12
|
+
when NO_COMPRESSION
|
|
13
|
+
Base64.decode64(data)
|
|
14
|
+
when GZIP_COMPRESSION
|
|
15
|
+
gz = Zlib::GzipReader.new(StringIO.new(Base64.decode64(data)))
|
|
16
|
+
gz.read
|
|
17
|
+
when ZLIB_COMPRESSION
|
|
18
|
+
Zlib::Inflate.inflate(Base64.decode64(data))
|
|
19
|
+
else
|
|
20
|
+
raise StandardError, 'Compression flag value is incorrect'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Helpers
|
|
5
|
+
class EvaluatorHelper
|
|
6
|
+
def self.matcher_type(condition, segments_repository, rb_segment_repository)
|
|
7
|
+
matchers = []
|
|
8
|
+
segments_repository.adapter.pipelined do
|
|
9
|
+
condition.matchers.each do |matcher|
|
|
10
|
+
matchers << if matcher[:negate]
|
|
11
|
+
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition,
|
|
12
|
+
matcher, segments_repository,
|
|
13
|
+
rb_segment_repository))
|
|
14
|
+
else
|
|
15
|
+
matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
final_matcher = condition.create_condition_matcher(matchers)
|
|
20
|
+
|
|
21
|
+
if final_matcher.nil?
|
|
22
|
+
config.logger.error('Invalid matcher type')
|
|
23
|
+
else
|
|
24
|
+
final_matcher
|
|
25
|
+
end
|
|
26
|
+
final_matcher
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.matcher_instance(type, condition, matcher, segments_repository, rb_segment_repository)
|
|
30
|
+
condition.send(
|
|
31
|
+
"matcher_#{type.downcase}",
|
|
32
|
+
matcher: matcher, segments_repository: segments_repository, rule_based_segments_repository: rb_segment_repository
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Helpers
|
|
5
|
+
class RepositoryHelper
|
|
6
|
+
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage)
|
|
7
|
+
to_add = []
|
|
8
|
+
to_delete = []
|
|
9
|
+
feature_flags.each do |feature_flag|
|
|
10
|
+
if Engine::Models::Split.archived?(feature_flag) || !feature_flag_repository.flag_set_filter.intersect?(feature_flag[:sets])
|
|
11
|
+
config.logger.debug("removing feature flag from store(#{feature_flag})") if config.debug_enabled
|
|
12
|
+
to_delete.push(feature_flag)
|
|
13
|
+
next
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
feature_flag = check_missing_elements(feature_flag, config)
|
|
17
|
+
|
|
18
|
+
config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
|
|
19
|
+
to_add.push(feature_flag)
|
|
20
|
+
end
|
|
21
|
+
feature_flag_repository.clear if clear_storage
|
|
22
|
+
feature_flag_repository.update(to_add, to_delete, change_number)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.check_missing_elements(feature_flag, config)
|
|
26
|
+
unless feature_flag.key?(:impressionsDisabled)
|
|
27
|
+
feature_flag[:impressionsDisabled] = false
|
|
28
|
+
if config.debug_enabled
|
|
29
|
+
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
unless feature_flag.key?(:prerequisites)
|
|
34
|
+
feature_flag[:prerequisites] = []
|
|
35
|
+
if config.debug_enabled
|
|
36
|
+
config.logger.debug("feature flag (#{feature_flag[:name]}) does not have prerequisites field, setting it to empty array")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
feature_flag
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config)
|
|
44
|
+
to_add = []
|
|
45
|
+
to_delete = []
|
|
46
|
+
rule_based_segments.each do |rule_based_segment|
|
|
47
|
+
if Engine::Models::Split.archived?(rule_based_segment)
|
|
48
|
+
config.logger.debug("removing rule based segment from store(#{rule_based_segment})") if config.debug_enabled
|
|
49
|
+
to_delete.push(rule_based_segment)
|
|
50
|
+
next
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled
|
|
54
|
+
to_add.push(rule_based_segment)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
rule_based_segment_repository.update(to_add, to_delete, change_number)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Helpers
|
|
5
|
+
class ThreadHelper
|
|
6
|
+
def self.stop(thread_sym, config)
|
|
7
|
+
thread = config.threads[thread_sym]
|
|
8
|
+
|
|
9
|
+
unless thread.nil?
|
|
10
|
+
config.logger.debug("Stopping #{thread_sym} thread...") if config.debug_enabled
|
|
11
|
+
Thread.kill(thread)
|
|
12
|
+
end
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
config.logger.error(e.inspect)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.alive?(thread_sym, config)
|
|
18
|
+
thread = config.threads[thread_sym]
|
|
19
|
+
|
|
20
|
+
thread.nil? ? false : thread.alive?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|