launchdarkly-server-sdk 8.11.2-java → 8.12.0-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 +4 -4
- data/lib/ldclient-rb/config.rb +69 -9
- data/lib/ldclient-rb/context.rb +1 -1
- data/lib/ldclient-rb/data_system.rb +227 -0
- data/lib/ldclient-rb/events.rb +34 -19
- data/lib/ldclient-rb/flags_state.rb +1 -1
- data/lib/ldclient-rb/impl/big_segments.rb +4 -4
- data/lib/ldclient-rb/impl/cache_store.rb +44 -0
- data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
- data/lib/ldclient-rb/impl/data_source/requestor.rb +113 -0
- data/lib/ldclient-rb/impl/data_source/status_provider.rb +83 -0
- data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
- data/lib/ldclient-rb/impl/data_source.rb +3 -3
- data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
- data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
- data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
- data/lib/ldclient-rb/impl/data_store/status_provider.rb +76 -0
- data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
- data/lib/ldclient-rb/impl/data_store.rb +11 -97
- data/lib/ldclient-rb/impl/data_system/data_source_builder_common.rb +77 -0
- data/lib/ldclient-rb/impl/data_system/fdv1.rb +20 -7
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +472 -0
- data/lib/ldclient-rb/impl/data_system/http_config_options.rb +32 -0
- data/lib/ldclient-rb/impl/data_system/polling.rb +628 -0
- data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
- data/lib/ldclient-rb/impl/data_system/streaming.rb +401 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
- data/lib/ldclient-rb/impl/evaluator.rb +3 -2
- data/lib/ldclient-rb/impl/event_sender.rb +14 -6
- data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
- data/lib/ldclient-rb/impl/integrations/file_data_source_v2.rb +460 -0
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +290 -0
- data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
- data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
- data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
- data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
- data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +71 -0
- data/lib/ldclient-rb/impl.rb +1 -2
- data/lib/ldclient-rb/in_memory_store.rb +1 -18
- data/lib/ldclient-rb/integrations/file_data.rb +67 -0
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
- data/lib/ldclient-rb/integrations/test_data.rb +11 -11
- data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
- data/lib/ldclient-rb/integrations/test_data_v2.rb +254 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
- data/lib/ldclient-rb/interfaces/data_system.rb +704 -0
- data/lib/ldclient-rb/interfaces/feature_store.rb +5 -2
- data/lib/ldclient-rb/ldclient.rb +66 -132
- data/lib/ldclient-rb/util.rb +11 -70
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +9 -17
- metadata +41 -19
- data/lib/ldclient-rb/cache_store.rb +0 -45
- data/lib/ldclient-rb/expiring_cache.rb +0 -77
- data/lib/ldclient-rb/memoized_value.rb +0 -32
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
- data/lib/ldclient-rb/polling.rb +0 -102
- data/lib/ldclient-rb/requestor.rb +0 -102
- data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
- data/lib/ldclient-rb/stream.rb +0 -197
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1725cd5066df8e5072abb09c606e0bda2784fb2b16640cdf6b557a4119e2356c
|
|
4
|
+
data.tar.gz: efbba66e2391376fdd945a7e26bbb429e121a127b267b1f14d7685d7f2098a3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aecbdb3e07abe04e7993fd7967ee10376bbb9e19e70558858fd11b16ee5a5f56327005c1b76df628bf3a5d552b6462b3d2e57a1d8dee0477e18454a0d3e30a66
|
|
7
|
+
data.tar.gz: 56b6cfad3066a766c76fdf70ab3f204778bf208cfda316eed9ba8bbf5106c8ccceb7c3fa436d6af8045093b61d741791a087df78cca21d937c47bb2eb018cf14
|
data/lib/ldclient-rb/config.rb
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
require "logger"
|
|
2
|
+
require "ldclient-rb/impl/cache_store"
|
|
3
|
+
require "ldclient-rb/impl/data_system/http_config_options"
|
|
4
|
+
require "ldclient-rb/impl/data_system/polling"
|
|
5
|
+
require "ldclient-rb/impl/data_system/streaming"
|
|
2
6
|
|
|
3
7
|
module LaunchDarkly
|
|
4
8
|
#
|
|
@@ -44,6 +48,7 @@ module LaunchDarkly
|
|
|
44
48
|
# @option opts [Hash] :application See {#application}
|
|
45
49
|
# @option opts [String] :payload_filter_key See {#payload_filter_key}
|
|
46
50
|
# @option opts [Boolean] :omit_anonymous_contexts See {#omit_anonymous_contexts}
|
|
51
|
+
# @option opts [DataSystemConfig] :data_system_config See {#data_system_config}
|
|
47
52
|
# @option hooks [Array<Interfaces::Hooks::Hook]
|
|
48
53
|
# @option plugins [Array<Interfaces::Plugins::Plugin]
|
|
49
54
|
#
|
|
@@ -82,6 +87,7 @@ module LaunchDarkly
|
|
|
82
87
|
@hooks = (opts[:hooks] || []).keep_if { |hook| hook.is_a? Interfaces::Hooks::Hook }
|
|
83
88
|
@plugins = (opts[:plugins] || []).keep_if { |plugin| plugin.is_a? Interfaces::Plugins::Plugin }
|
|
84
89
|
@omit_anonymous_contexts = opts.has_key?(:omit_anonymous_contexts) && opts[:omit_anonymous_contexts]
|
|
90
|
+
@data_system_config = opts[:data_system_config]
|
|
85
91
|
@data_source_update_sink = nil
|
|
86
92
|
@instance_id = nil
|
|
87
93
|
end
|
|
@@ -96,7 +102,7 @@ module LaunchDarkly
|
|
|
96
102
|
# Custom data source implementations should integrate with this sink if
|
|
97
103
|
# they want to provide support for data source status listeners.
|
|
98
104
|
#
|
|
99
|
-
# @private
|
|
105
|
+
# @api private
|
|
100
106
|
#
|
|
101
107
|
attr_accessor :data_source_update_sink
|
|
102
108
|
|
|
@@ -108,7 +114,7 @@ module LaunchDarkly
|
|
|
108
114
|
# property is not supported; it is temporarily being exposed to maintain
|
|
109
115
|
# backwards compatibility while the SDK structure is updated.
|
|
110
116
|
#
|
|
111
|
-
# @private
|
|
117
|
+
# @api private
|
|
112
118
|
#
|
|
113
119
|
attr_accessor :instance_id
|
|
114
120
|
|
|
@@ -430,6 +436,15 @@ module LaunchDarkly
|
|
|
430
436
|
#
|
|
431
437
|
attr_reader :omit_anonymous_contexts
|
|
432
438
|
|
|
439
|
+
#
|
|
440
|
+
# Configuration for the upcoming enhanced data system design. This is
|
|
441
|
+
# experimental and should not be set without direction from LaunchDarkly
|
|
442
|
+
# support.
|
|
443
|
+
#
|
|
444
|
+
# @return [DataSystemConfig, nil]
|
|
445
|
+
#
|
|
446
|
+
attr_reader :data_system_config
|
|
447
|
+
|
|
433
448
|
|
|
434
449
|
#
|
|
435
450
|
# The default LaunchDarkly client configuration. This configuration sets
|
|
@@ -453,7 +468,7 @@ module LaunchDarkly
|
|
|
453
468
|
# @return [String] "https://sdk.launchdarkly.com"
|
|
454
469
|
#
|
|
455
470
|
def self.default_base_uri
|
|
456
|
-
|
|
471
|
+
Impl::DataSystem::PollingDataSourceBuilder::DEFAULT_BASE_URI
|
|
457
472
|
end
|
|
458
473
|
|
|
459
474
|
#
|
|
@@ -461,7 +476,7 @@ module LaunchDarkly
|
|
|
461
476
|
# @return [String] "https://stream.launchdarkly.com"
|
|
462
477
|
#
|
|
463
478
|
def self.default_stream_uri
|
|
464
|
-
|
|
479
|
+
Impl::DataSystem::StreamingDataSourceBuilder::DEFAULT_BASE_URI
|
|
465
480
|
end
|
|
466
481
|
|
|
467
482
|
#
|
|
@@ -477,7 +492,7 @@ module LaunchDarkly
|
|
|
477
492
|
# @return [Object] the Rails cache if in Rails, or a simple in-memory implementation otherwise
|
|
478
493
|
#
|
|
479
494
|
def self.default_cache_store
|
|
480
|
-
defined?(Rails) && Rails.respond_to?(:cache) ? Rails.cache : ThreadSafeMemoryStore.new
|
|
495
|
+
defined?(Rails) && Rails.respond_to?(:cache) ? Rails.cache : Impl::ThreadSafeMemoryStore.new
|
|
481
496
|
end
|
|
482
497
|
|
|
483
498
|
#
|
|
@@ -493,7 +508,7 @@ module LaunchDarkly
|
|
|
493
508
|
# @return [Float] 10
|
|
494
509
|
#
|
|
495
510
|
def self.default_read_timeout
|
|
496
|
-
|
|
511
|
+
Impl::DataSystem::HttpConfigOptions::DEFAULT_READ_TIMEOUT
|
|
497
512
|
end
|
|
498
513
|
|
|
499
514
|
#
|
|
@@ -501,7 +516,7 @@ module LaunchDarkly
|
|
|
501
516
|
# @return [Float] 1
|
|
502
517
|
#
|
|
503
518
|
def self.default_initial_reconnect_delay
|
|
504
|
-
|
|
519
|
+
Impl::DataSystem::StreamingDataSourceBuilder::DEFAULT_INITIAL_RECONNECT_DELAY
|
|
505
520
|
end
|
|
506
521
|
|
|
507
522
|
#
|
|
@@ -509,7 +524,7 @@ module LaunchDarkly
|
|
|
509
524
|
# @return [Float] 2
|
|
510
525
|
#
|
|
511
526
|
def self.default_connect_timeout
|
|
512
|
-
|
|
527
|
+
Impl::DataSystem::HttpConfigOptions::DEFAULT_CONNECT_TIMEOUT
|
|
513
528
|
end
|
|
514
529
|
|
|
515
530
|
#
|
|
@@ -563,7 +578,7 @@ module LaunchDarkly
|
|
|
563
578
|
# @return [Float] 30
|
|
564
579
|
#
|
|
565
580
|
def self.default_poll_interval
|
|
566
|
-
|
|
581
|
+
Impl::DataSystem::PollingDataSourceBuilder::DEFAULT_POLL_INTERVAL
|
|
567
582
|
end
|
|
568
583
|
|
|
569
584
|
#
|
|
@@ -678,4 +693,49 @@ module LaunchDarkly
|
|
|
678
693
|
# @return [Float]
|
|
679
694
|
attr_reader :stale_after
|
|
680
695
|
end
|
|
696
|
+
|
|
697
|
+
#
|
|
698
|
+
# Configuration for LaunchDarkly's data acquisition strategy.
|
|
699
|
+
#
|
|
700
|
+
# This is not stable and is not subject to any backwards compatibility guarantees
|
|
701
|
+
# or semantic versioning. It is not suitable for production usage.
|
|
702
|
+
#
|
|
703
|
+
class DataSystemConfig
|
|
704
|
+
#
|
|
705
|
+
# @param initializers [Array<#build(String, Config)>, nil] The (optional) array of builders
|
|
706
|
+
# @param synchronizers [Array<#build(String, Config)>, nil] The (optional) array of synchronizer builders
|
|
707
|
+
# @param data_store_mode [Symbol] The (optional) data store mode
|
|
708
|
+
# @param data_store [LaunchDarkly::Interfaces::FeatureStore, nil] The (optional) data store
|
|
709
|
+
# @param fdv1_fallback_synchronizer [#build(String, Config), nil]
|
|
710
|
+
# The (optional) builder for FDv1-compatible fallback synchronizer
|
|
711
|
+
#
|
|
712
|
+
def initialize(initializers: nil, synchronizers: nil,
|
|
713
|
+
data_store_mode: LaunchDarkly::Interfaces::DataSystem::DataStoreMode::READ_ONLY, data_store: nil, fdv1_fallback_synchronizer: nil)
|
|
714
|
+
@initializers = initializers
|
|
715
|
+
@synchronizers = synchronizers
|
|
716
|
+
@data_store_mode = data_store_mode
|
|
717
|
+
@data_store = data_store
|
|
718
|
+
@fdv1_fallback_synchronizer = fdv1_fallback_synchronizer
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
# The initializer builders for the data system. Each builder responds to build(sdk_key, config) and returns an Initializer.
|
|
722
|
+
# @return [Array<#build(String, Config)>, nil]
|
|
723
|
+
attr_reader :initializers
|
|
724
|
+
|
|
725
|
+
# The synchronizer builders for the data system. Each builder responds to build(sdk_key, config) and returns a Synchronizer.
|
|
726
|
+
# @return [Array<#build(String, Config)>, nil]
|
|
727
|
+
attr_reader :synchronizers
|
|
728
|
+
|
|
729
|
+
# The data store mode.
|
|
730
|
+
# @return [Symbol]
|
|
731
|
+
attr_reader :data_store_mode
|
|
732
|
+
|
|
733
|
+
# The data store.
|
|
734
|
+
# @return [LaunchDarkly::Interfaces::FeatureStore, nil]
|
|
735
|
+
attr_reader :data_store
|
|
736
|
+
|
|
737
|
+
# The FDv1-compatible fallback synchronizer builder. Responds to build(sdk_key, config) and returns a Synchronizer.
|
|
738
|
+
# @return [#build(String, Config), nil]
|
|
739
|
+
attr_reader :fdv1_fallback_synchronizer
|
|
740
|
+
end
|
|
681
741
|
end
|
data/lib/ldclient-rb/context.rb
CHANGED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ldclient-rb/interfaces/data_system'
|
|
4
|
+
require 'ldclient-rb/config'
|
|
5
|
+
require 'ldclient-rb/impl/data_system/polling'
|
|
6
|
+
require 'ldclient-rb/impl/data_system/streaming'
|
|
7
|
+
|
|
8
|
+
module LaunchDarkly
|
|
9
|
+
#
|
|
10
|
+
# Configuration for LaunchDarkly's data acquisition strategy.
|
|
11
|
+
#
|
|
12
|
+
# This module provides factory methods for creating data system configurations.
|
|
13
|
+
#
|
|
14
|
+
module DataSystem
|
|
15
|
+
#
|
|
16
|
+
# Builder for the data system configuration.
|
|
17
|
+
#
|
|
18
|
+
class ConfigBuilder
|
|
19
|
+
def initialize
|
|
20
|
+
@initializers = nil
|
|
21
|
+
@synchronizers = nil
|
|
22
|
+
@fdv1_fallback_synchronizer = nil
|
|
23
|
+
@data_store_mode = LaunchDarkly::Interfaces::DataSystem::DataStoreMode::READ_ONLY
|
|
24
|
+
@data_store = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Sets the initializers for the data system.
|
|
29
|
+
#
|
|
30
|
+
# @param initializers [Array<#build(String, Config)>]
|
|
31
|
+
# Array of builders that respond to build(sdk_key, config) and return an Initializer
|
|
32
|
+
# @return [ConfigBuilder] self for chaining
|
|
33
|
+
#
|
|
34
|
+
def initializers(initializers)
|
|
35
|
+
@initializers = initializers
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Sets the synchronizers for the data system.
|
|
41
|
+
#
|
|
42
|
+
# @param synchronizers [Array<#build(String, Config)>]
|
|
43
|
+
# Array of builders that respond to build(sdk_key, config) and return a Synchronizer
|
|
44
|
+
# @return [ConfigBuilder] self for chaining
|
|
45
|
+
#
|
|
46
|
+
def synchronizers(synchronizers)
|
|
47
|
+
@synchronizers = synchronizers
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# Configures the SDK with a fallback synchronizer that is compatible with
|
|
53
|
+
# the Flag Delivery v1 API.
|
|
54
|
+
#
|
|
55
|
+
# @param fallback [#build(String, Config)] Builder that responds to build(sdk_key, config) and returns the fallback Synchronizer
|
|
56
|
+
# @return [ConfigBuilder] self for chaining
|
|
57
|
+
#
|
|
58
|
+
def fdv1_compatible_synchronizer(fallback)
|
|
59
|
+
@fdv1_fallback_synchronizer = fallback
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
#
|
|
64
|
+
# Sets the data store configuration for the data system.
|
|
65
|
+
#
|
|
66
|
+
# @param data_store [LaunchDarkly::Interfaces::FeatureStore] The data store
|
|
67
|
+
# @param store_mode [Symbol] The store mode
|
|
68
|
+
# @return [ConfigBuilder] self for chaining
|
|
69
|
+
#
|
|
70
|
+
def data_store(data_store, store_mode)
|
|
71
|
+
@data_store = data_store
|
|
72
|
+
@data_store_mode = store_mode
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
# Builds the data system configuration.
|
|
78
|
+
#
|
|
79
|
+
# @return [DataSystemConfig]
|
|
80
|
+
#
|
|
81
|
+
def build
|
|
82
|
+
DataSystemConfig.new(
|
|
83
|
+
initializers: @initializers,
|
|
84
|
+
synchronizers: @synchronizers,
|
|
85
|
+
data_store_mode: @data_store_mode,
|
|
86
|
+
data_store: @data_store,
|
|
87
|
+
fdv1_fallback_synchronizer: @fdv1_fallback_synchronizer
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Returns a builder for creating a polling data source.
|
|
94
|
+
# This is a building block that can be used with {ConfigBuilder#initializers}
|
|
95
|
+
# or {ConfigBuilder#synchronizers} to create custom data system configurations.
|
|
96
|
+
#
|
|
97
|
+
# @return [LaunchDarkly::Impl::DataSystem::PollingDataSourceBuilder]
|
|
98
|
+
#
|
|
99
|
+
def self.polling_ds_builder
|
|
100
|
+
LaunchDarkly::Impl::DataSystem::PollingDataSourceBuilder.new
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
# Returns a builder for creating an FDv1 fallback polling data source.
|
|
105
|
+
# This is a building block that can be used with {ConfigBuilder#fdv1_compatible_synchronizer}
|
|
106
|
+
# to provide FDv1 compatibility in custom data system configurations.
|
|
107
|
+
#
|
|
108
|
+
# @return [LaunchDarkly::Impl::DataSystem::FDv1PollingDataSourceBuilder]
|
|
109
|
+
#
|
|
110
|
+
def self.fdv1_fallback_ds_builder
|
|
111
|
+
LaunchDarkly::Impl::DataSystem::FDv1PollingDataSourceBuilder.new
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
#
|
|
115
|
+
# Returns a builder for creating a streaming data source.
|
|
116
|
+
# This is a building block that can be used with {ConfigBuilder#synchronizers}
|
|
117
|
+
# to create custom data system configurations.
|
|
118
|
+
#
|
|
119
|
+
# @return [LaunchDarkly::Impl::DataSystem::StreamingDataSourceBuilder]
|
|
120
|
+
#
|
|
121
|
+
def self.streaming_ds_builder
|
|
122
|
+
LaunchDarkly::Impl::DataSystem::StreamingDataSourceBuilder.new
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
# Default is LaunchDarkly's recommended flag data acquisition strategy.
|
|
127
|
+
#
|
|
128
|
+
# Currently, it operates a two-phase method for obtaining data: first, it
|
|
129
|
+
# requests data from LaunchDarkly's global CDN. Then, it initiates a
|
|
130
|
+
# streaming connection to LaunchDarkly's Flag Delivery services to
|
|
131
|
+
# receive real-time updates.
|
|
132
|
+
#
|
|
133
|
+
# If the streaming connection is interrupted for an extended period of
|
|
134
|
+
# time, the SDK will automatically fall back to polling the global CDN
|
|
135
|
+
# for updates.
|
|
136
|
+
#
|
|
137
|
+
# @return [ConfigBuilder]
|
|
138
|
+
#
|
|
139
|
+
def self.default
|
|
140
|
+
polling_builder = polling_ds_builder
|
|
141
|
+
streaming_builder = streaming_ds_builder
|
|
142
|
+
fallback = fdv1_fallback_ds_builder
|
|
143
|
+
|
|
144
|
+
builder = ConfigBuilder.new
|
|
145
|
+
builder.initializers([polling_builder])
|
|
146
|
+
builder.synchronizers([streaming_builder, polling_builder])
|
|
147
|
+
builder.fdv1_compatible_synchronizer(fallback)
|
|
148
|
+
|
|
149
|
+
builder
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Streaming configures the SDK to efficiently stream flag/segment data
|
|
154
|
+
# in the background, allowing evaluations to operate on the latest data
|
|
155
|
+
# with no additional latency.
|
|
156
|
+
#
|
|
157
|
+
# @return [ConfigBuilder]
|
|
158
|
+
#
|
|
159
|
+
def self.streaming
|
|
160
|
+
streaming_builder = streaming_ds_builder
|
|
161
|
+
fallback = fdv1_fallback_ds_builder
|
|
162
|
+
|
|
163
|
+
builder = ConfigBuilder.new
|
|
164
|
+
builder.synchronizers([streaming_builder])
|
|
165
|
+
builder.fdv1_compatible_synchronizer(fallback)
|
|
166
|
+
|
|
167
|
+
builder
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
#
|
|
171
|
+
# Polling configures the SDK to regularly poll an endpoint for
|
|
172
|
+
# flag/segment data in the background. This is less efficient than
|
|
173
|
+
# streaming, but may be necessary in some network environments.
|
|
174
|
+
#
|
|
175
|
+
# @return [ConfigBuilder]
|
|
176
|
+
#
|
|
177
|
+
def self.polling
|
|
178
|
+
polling_builder = polling_ds_builder
|
|
179
|
+
fallback = fdv1_fallback_ds_builder
|
|
180
|
+
|
|
181
|
+
builder = ConfigBuilder.new
|
|
182
|
+
builder.synchronizers([polling_builder])
|
|
183
|
+
builder.fdv1_compatible_synchronizer(fallback)
|
|
184
|
+
|
|
185
|
+
builder
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
#
|
|
189
|
+
# Custom returns a builder suitable for creating a custom data
|
|
190
|
+
# acquisition strategy. You may configure how the SDK uses a Persistent
|
|
191
|
+
# Store, how the SDK obtains an initial set of data, and how the SDK
|
|
192
|
+
# keeps data up-to-date.
|
|
193
|
+
#
|
|
194
|
+
# @return [ConfigBuilder]
|
|
195
|
+
#
|
|
196
|
+
def self.custom
|
|
197
|
+
ConfigBuilder.new
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
#
|
|
201
|
+
# Daemon configures the SDK to read from a persistent store integration
|
|
202
|
+
# that is populated by Relay Proxy or other SDKs. The SDK will not connect
|
|
203
|
+
# to LaunchDarkly. In this mode, the SDK never writes to the data store.
|
|
204
|
+
#
|
|
205
|
+
# @param store [Object] The persistent store
|
|
206
|
+
# @return [ConfigBuilder]
|
|
207
|
+
#
|
|
208
|
+
def self.daemon(store)
|
|
209
|
+
custom.data_store(store, LaunchDarkly::Interfaces::DataSystem::DataStoreMode::READ_ONLY)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
# PersistentStore is similar to default, with the addition of a persistent
|
|
214
|
+
# store integration. Before data has arrived from LaunchDarkly, the SDK is
|
|
215
|
+
# able to evaluate flags using data from the persistent store. Once fresh
|
|
216
|
+
# data is available, the SDK will no longer read from the persistent store,
|
|
217
|
+
# although it will keep it up-to-date.
|
|
218
|
+
#
|
|
219
|
+
# @param store [Object] The persistent store
|
|
220
|
+
# @return [ConfigBuilder]
|
|
221
|
+
#
|
|
222
|
+
def self.persistent_store(store)
|
|
223
|
+
default.data_store(store, LaunchDarkly::Interfaces::DataSystem::DataStoreMode::READ_WRITE)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
data/lib/ldclient-rb/events.rb
CHANGED
|
@@ -3,6 +3,8 @@ require "ldclient-rb/impl/diagnostic_events"
|
|
|
3
3
|
require "ldclient-rb/impl/event_sender"
|
|
4
4
|
require "ldclient-rb/impl/event_summarizer"
|
|
5
5
|
require "ldclient-rb/impl/event_types"
|
|
6
|
+
require "ldclient-rb/impl/non_blocking_thread_pool"
|
|
7
|
+
require "ldclient-rb/impl/simple_lru_cache"
|
|
6
8
|
require "ldclient-rb/impl/util"
|
|
7
9
|
|
|
8
10
|
require "concurrent"
|
|
@@ -60,6 +62,19 @@ module LaunchDarkly
|
|
|
60
62
|
def record_migration_op_event(event)
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
#
|
|
66
|
+
# Tells the event processor that all pending analytics events should be delivered as soon as possible.
|
|
67
|
+
#
|
|
68
|
+
# When the LaunchDarkly client generates analytics events (from {LaunchDarkly::LDClient#variation},
|
|
69
|
+
# {LaunchDarkly::LDClient#variation_detail}, {LaunchDarkly::LDClient#identify}, or
|
|
70
|
+
# {LaunchDarkly::LDClient#track}), they are queued on a worker thread. The event thread normally
|
|
71
|
+
# sends all queued events to LaunchDarkly at regular intervals, controlled by the
|
|
72
|
+
# {LaunchDarkly::Config#flush_interval} option. Calling `flush` triggers a send without waiting
|
|
73
|
+
# for the next interval.
|
|
74
|
+
#
|
|
75
|
+
# Flushing is asynchronous, so this method will return before it is complete. However, if you
|
|
76
|
+
# call {LaunchDarkly::LDClient#close}, events are guaranteed to be sent before that method returns.
|
|
77
|
+
#
|
|
63
78
|
def flush
|
|
64
79
|
end
|
|
65
80
|
|
|
@@ -70,24 +85,24 @@ module LaunchDarkly
|
|
|
70
85
|
MAX_FLUSH_WORKERS = 5
|
|
71
86
|
private_constant :MAX_FLUSH_WORKERS
|
|
72
87
|
|
|
73
|
-
# @private
|
|
88
|
+
# @api private
|
|
74
89
|
class NullEventProcessor
|
|
75
90
|
include EventProcessorMethods
|
|
76
91
|
end
|
|
77
92
|
|
|
78
|
-
# @private
|
|
93
|
+
# @api private
|
|
79
94
|
class FlushMessage
|
|
80
95
|
end
|
|
81
96
|
|
|
82
|
-
# @private
|
|
97
|
+
# @api private
|
|
83
98
|
class FlushContextsMessage
|
|
84
99
|
end
|
|
85
100
|
|
|
86
|
-
# @private
|
|
101
|
+
# @api private
|
|
87
102
|
class DiagnosticEventMessage
|
|
88
103
|
end
|
|
89
104
|
|
|
90
|
-
# @private
|
|
105
|
+
# @api private
|
|
91
106
|
class SynchronousMessage
|
|
92
107
|
def initialize
|
|
93
108
|
@reply = Concurrent::Semaphore.new(0)
|
|
@@ -102,15 +117,15 @@ module LaunchDarkly
|
|
|
102
117
|
end
|
|
103
118
|
end
|
|
104
119
|
|
|
105
|
-
# @private
|
|
120
|
+
# @api private
|
|
106
121
|
class TestSyncMessage < SynchronousMessage
|
|
107
122
|
end
|
|
108
123
|
|
|
109
|
-
# @private
|
|
124
|
+
# @api private
|
|
110
125
|
class StopMessage < SynchronousMessage
|
|
111
126
|
end
|
|
112
127
|
|
|
113
|
-
# @private
|
|
128
|
+
# @api private
|
|
114
129
|
class EventProcessor
|
|
115
130
|
include EventProcessorMethods
|
|
116
131
|
|
|
@@ -141,7 +156,7 @@ module LaunchDarkly
|
|
|
141
156
|
@inbox_full = Concurrent::AtomicBoolean.new(false)
|
|
142
157
|
|
|
143
158
|
event_sender = (test_properties || {})[:event_sender] ||
|
|
144
|
-
Impl::EventSender.new(sdk_key, config
|
|
159
|
+
Impl::EventSender.new(sdk_key, config)
|
|
145
160
|
|
|
146
161
|
@timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
|
|
147
162
|
@omit_anonymous_contexts = config.omit_anonymous_contexts
|
|
@@ -226,7 +241,7 @@ module LaunchDarkly
|
|
|
226
241
|
end
|
|
227
242
|
end
|
|
228
243
|
|
|
229
|
-
# @private
|
|
244
|
+
# @api private
|
|
230
245
|
class EventDispatcher
|
|
231
246
|
def initialize(inbox, sdk_key, config, diagnostic_accumulator, event_sender)
|
|
232
247
|
@sdk_key = sdk_key
|
|
@@ -235,7 +250,7 @@ module LaunchDarkly
|
|
|
235
250
|
@event_sender = event_sender
|
|
236
251
|
@sampler = LaunchDarkly::Impl::Sampler.new(Random.new)
|
|
237
252
|
|
|
238
|
-
@context_keys = SimpleLRUCacheSet.new(config.context_keys_capacity)
|
|
253
|
+
@context_keys = Impl::SimpleLRUCacheSet.new(config.context_keys_capacity)
|
|
239
254
|
@formatter = EventOutputFormatter.new(config)
|
|
240
255
|
@disabled = Concurrent::AtomicBoolean.new(false)
|
|
241
256
|
@last_known_past_time = Concurrent::AtomicReference.new(0)
|
|
@@ -243,10 +258,10 @@ module LaunchDarkly
|
|
|
243
258
|
@events_in_last_batch = 0
|
|
244
259
|
|
|
245
260
|
outbox = EventBuffer.new(config.capacity, config.logger)
|
|
246
|
-
flush_workers = NonBlockingThreadPool.new(MAX_FLUSH_WORKERS, 'LD/EventDispatcher/FlushWorkers')
|
|
261
|
+
flush_workers = Impl::NonBlockingThreadPool.new(MAX_FLUSH_WORKERS, 'LD/EventDispatcher/FlushWorkers')
|
|
247
262
|
|
|
248
263
|
if !@diagnostic_accumulator.nil?
|
|
249
|
-
diagnostic_event_workers = NonBlockingThreadPool.new(1, 'LD/EventDispatcher/DiagnosticEventWorkers')
|
|
264
|
+
diagnostic_event_workers = Impl::NonBlockingThreadPool.new(1, 'LD/EventDispatcher/DiagnosticEventWorkers')
|
|
250
265
|
init_event = @diagnostic_accumulator.create_init_event(config)
|
|
251
266
|
send_diagnostic_event(init_event, diagnostic_event_workers)
|
|
252
267
|
else
|
|
@@ -281,7 +296,7 @@ module LaunchDarkly
|
|
|
281
296
|
dispatch_event(message, outbox)
|
|
282
297
|
end
|
|
283
298
|
rescue => e
|
|
284
|
-
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
299
|
+
Impl::Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
285
300
|
end
|
|
286
301
|
end
|
|
287
302
|
end
|
|
@@ -383,7 +398,7 @@ module LaunchDarkly
|
|
|
383
398
|
@last_known_past_time.value = (result.time_from_server.to_f * 1000).to_i
|
|
384
399
|
end
|
|
385
400
|
rescue => e
|
|
386
|
-
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
401
|
+
Impl::Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
387
402
|
end
|
|
388
403
|
end
|
|
389
404
|
outbox.clear if success # Reset our internal state, these events now belong to the flush worker
|
|
@@ -408,16 +423,16 @@ module LaunchDarkly
|
|
|
408
423
|
begin
|
|
409
424
|
@event_sender.send_event_data(event.to_json, "diagnostic event", true)
|
|
410
425
|
rescue => e
|
|
411
|
-
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
426
|
+
Impl::Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
|
412
427
|
end
|
|
413
428
|
end
|
|
414
429
|
end
|
|
415
430
|
end
|
|
416
431
|
|
|
417
|
-
# @private
|
|
432
|
+
# @api private
|
|
418
433
|
FlushPayload = Struct.new(:events, :summary)
|
|
419
434
|
|
|
420
|
-
# @private
|
|
435
|
+
# @api private
|
|
421
436
|
class EventBuffer
|
|
422
437
|
def initialize(capacity, logger)
|
|
423
438
|
@capacity = capacity
|
|
@@ -461,7 +476,7 @@ module LaunchDarkly
|
|
|
461
476
|
end
|
|
462
477
|
end
|
|
463
478
|
|
|
464
|
-
# @private
|
|
479
|
+
# @api private
|
|
465
480
|
class EventOutputFormatter
|
|
466
481
|
FEATURE_KIND = 'feature'
|
|
467
482
|
IDENTIFY_KIND = 'identify'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require "ldclient-rb/config"
|
|
2
|
-
require "ldclient-rb/expiring_cache"
|
|
2
|
+
require "ldclient-rb/impl/expiring_cache"
|
|
3
3
|
require "ldclient-rb/impl/repeating_task"
|
|
4
|
+
require "ldclient-rb/impl/util"
|
|
4
5
|
require "ldclient-rb/interfaces"
|
|
5
|
-
require "ldclient-rb/util"
|
|
6
6
|
|
|
7
7
|
require "digest"
|
|
8
8
|
|
|
@@ -45,7 +45,7 @@ module LaunchDarkly
|
|
|
45
45
|
membership = EMPTY_MEMBERSHIP if membership.nil?
|
|
46
46
|
@cache[context_key] = membership
|
|
47
47
|
rescue => e
|
|
48
|
-
|
|
48
|
+
Impl::Util.log_exception(@logger, "Big Segment store membership query returned error", e)
|
|
49
49
|
return BigSegmentMembershipResult.new(nil, BigSegmentsStatus::STORE_ERROR)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
@@ -67,7 +67,7 @@ module LaunchDarkly
|
|
|
67
67
|
metadata = @store.get_metadata
|
|
68
68
|
new_status = Interfaces::BigSegmentStoreStatus.new(true, !metadata || stale?(metadata.last_up_to_date))
|
|
69
69
|
rescue => e
|
|
70
|
-
|
|
70
|
+
Impl::Util.log_exception(@logger, "Big Segment store status query returned error", e)
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
@last_status = new_status
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "concurrent/map"
|
|
2
|
+
|
|
3
|
+
module LaunchDarkly
|
|
4
|
+
module Impl
|
|
5
|
+
# A thread-safe in-memory store that uses the same semantics that Faraday would expect, although we
|
|
6
|
+
# no longer use Faraday. This is used by Requestor, when we are not in a Rails environment.
|
|
7
|
+
class ThreadSafeMemoryStore
|
|
8
|
+
#
|
|
9
|
+
# Default constructor
|
|
10
|
+
#
|
|
11
|
+
# @return [ThreadSafeMemoryStore] a new store
|
|
12
|
+
def initialize
|
|
13
|
+
@cache = Concurrent::Map.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Read a value from the cache
|
|
18
|
+
# @param key [Object] the cache key
|
|
19
|
+
#
|
|
20
|
+
# @return [Object] the cache value
|
|
21
|
+
def read(key)
|
|
22
|
+
@cache[key]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Store a value in the cache
|
|
27
|
+
# @param key [Object] the cache key
|
|
28
|
+
# @param value [Object] the value to associate with the key
|
|
29
|
+
#
|
|
30
|
+
# @return [Object] the value
|
|
31
|
+
def write(key, value)
|
|
32
|
+
@cache[key] = value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Delete a value in the cache
|
|
37
|
+
# @param key [Object] the cache key
|
|
38
|
+
def delete(key)
|
|
39
|
+
@cache.delete(key)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|