launchdarkly-server-sdk 8.11.2 → 8.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ldclient-rb/config.rb +66 -3
  3. data/lib/ldclient-rb/context.rb +1 -1
  4. data/lib/ldclient-rb/data_system.rb +243 -0
  5. data/lib/ldclient-rb/events.rb +34 -19
  6. data/lib/ldclient-rb/flags_state.rb +1 -1
  7. data/lib/ldclient-rb/impl/big_segments.rb +4 -4
  8. data/lib/ldclient-rb/impl/cache_store.rb +44 -0
  9. data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
  10. data/lib/ldclient-rb/impl/data_source/requestor.rb +106 -0
  11. data/lib/ldclient-rb/impl/data_source/status_provider.rb +78 -0
  12. data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
  13. data/lib/ldclient-rb/impl/data_source.rb +3 -3
  14. data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
  15. data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
  16. data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
  17. data/lib/ldclient-rb/impl/data_store/status_provider.rb +82 -0
  18. data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
  19. data/lib/ldclient-rb/impl/data_store.rb +11 -97
  20. data/lib/ldclient-rb/impl/data_system/fdv1.rb +20 -7
  21. data/lib/ldclient-rb/impl/data_system/fdv2.rb +471 -0
  22. data/lib/ldclient-rb/impl/data_system/polling.rb +601 -0
  23. data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
  24. data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
  25. data/lib/ldclient-rb/impl/evaluator.rb +3 -2
  26. data/lib/ldclient-rb/impl/event_sender.rb +4 -3
  27. data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
  28. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
  29. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +288 -0
  30. data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
  31. data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
  32. data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
  33. data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
  34. data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
  35. data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
  36. data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
  37. data/lib/ldclient-rb/impl/util.rb +65 -0
  38. data/lib/ldclient-rb/impl.rb +1 -2
  39. data/lib/ldclient-rb/in_memory_store.rb +1 -18
  40. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
  41. data/lib/ldclient-rb/integrations/test_data.rb +11 -11
  42. data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
  43. data/lib/ldclient-rb/integrations/test_data_v2.rb +248 -0
  44. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
  45. data/lib/ldclient-rb/interfaces/data_system.rb +755 -0
  46. data/lib/ldclient-rb/interfaces/feature_store.rb +3 -0
  47. data/lib/ldclient-rb/ldclient.rb +55 -131
  48. data/lib/ldclient-rb/util.rb +11 -70
  49. data/lib/ldclient-rb/version.rb +1 -1
  50. data/lib/ldclient-rb.rb +8 -17
  51. metadata +35 -17
  52. data/lib/ldclient-rb/cache_store.rb +0 -45
  53. data/lib/ldclient-rb/expiring_cache.rb +0 -77
  54. data/lib/ldclient-rb/memoized_value.rb +0 -32
  55. data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
  56. data/lib/ldclient-rb/polling.rb +0 -102
  57. data/lib/ldclient-rb/requestor.rb +0 -102
  58. data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
  59. data/lib/ldclient-rb/stream.rb +0 -197
@@ -103,6 +103,9 @@ module LaunchDarkly
103
103
  #
104
104
  # Performs any necessary cleanup to shut down the store when the client is being shut down.
105
105
  #
106
+ # This method should be idempotent - it is safe to call it multiple times, and subsequent
107
+ # calls after the first should have no effect.
108
+ #
106
109
  # @return [void]
107
110
  #
108
111
  def stop
@@ -1,14 +1,16 @@
1
1
  require "ldclient-rb/impl/big_segments"
2
2
  require "ldclient-rb/impl/broadcaster"
3
+ require "ldclient-rb/impl/context"
3
4
  require "ldclient-rb/impl/data_source"
4
5
  require "ldclient-rb/impl/data_store"
5
- require "ldclient-rb/impl/data_source/null_processor"
6
+ require "ldclient-rb/impl/data_system/fdv1"
6
7
  require "ldclient-rb/impl/diagnostic_events"
7
- require "ldclient-rb/impl/evaluator"
8
8
  require "ldclient-rb/impl/evaluation_with_hook_result"
9
+ require "ldclient-rb/impl/evaluator"
9
10
  require "ldclient-rb/impl/flag_tracker"
10
- require "ldclient-rb/impl/store_client_wrapper"
11
11
  require "ldclient-rb/impl/migrations/tracker"
12
+ require "ldclient-rb/impl/util"
13
+ require "ldclient-rb/events"
12
14
  require "concurrent"
13
15
  require "concurrent/atomics"
14
16
  require "digest/sha1"
@@ -30,6 +32,18 @@ module LaunchDarkly
30
32
 
31
33
  def_delegators :@config, :logger
32
34
 
35
+ # @!method flush
36
+ # Delegates to {LaunchDarkly::EventProcessorMethods#flush}.
37
+ def_delegator :@event_processor, :flush
38
+
39
+ # @!method data_store_status_provider
40
+ # Delegates to the data system {LaunchDarkly::Impl::DataSystem#data_store_status_provider}.
41
+ # @return [LaunchDarkly::Interfaces::DataStore::StatusProvider]
42
+ # @!method data_source_status_provider
43
+ # Delegates to the data system {LaunchDarkly::Impl::DataSystem#data_source_status_provider}.
44
+ # @return [LaunchDarkly::Interfaces::DataSource::StatusProvider]
45
+ def_delegators :@data_system, :data_store_status_provider, :data_source_status_provider
46
+
33
47
  #
34
48
  # Creates a new client instance that connects to LaunchDarkly. A custom
35
49
  # configuration parameter can also supplied to specify advanced options,
@@ -48,13 +62,16 @@ module LaunchDarkly
48
62
  #
49
63
  def initialize(sdk_key, config = Config.default, wait_for_sec = 5)
50
64
  # Note that sdk_key is normally a required parameter, and a nil value would cause the SDK to
51
- # fail in most configurations. However, there are some configurations where it would be OK
52
- # (offline = true, *or* we are using LDD mode or the file data source and events are disabled
53
- # so we're not connecting to any LD services) so rather than try to check for all of those
54
- # up front, we will let the constructors for the data source implementations implement this
55
- # fail-fast as appropriate, and just check here for the part regarding events.
56
- if !config.offline? && config.send_events
57
- raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil?
65
+ # fail in most configurations. However, there are some configurations where it would be OK to
66
+ # not provide a SDK key.
67
+ # * Offline mode
68
+ # * Using LDD mode with events disabled
69
+ # * Using a custom data source (like FileData) with events disabled
70
+ if !config.offline? && sdk_key.nil?
71
+ # If the data source is nil we create a default data source which requires the SDK key.
72
+ if config.send_events || (!config.use_ldd? && config.data_source.nil?)
73
+ raise ArgumentError, "sdk_key must not be nil"
74
+ end
58
75
  end
59
76
 
60
77
  @sdk_key = sdk_key
@@ -82,9 +99,10 @@ module LaunchDarkly
82
99
  # @param wait_for_sec [Float] maximum time (in seconds) to wait for initialization
83
100
  #
84
101
  def postfork(wait_for_sec = 5)
85
- @data_source = nil
102
+ @data_system = nil
86
103
  @event_processor = nil
87
104
  @big_segment_store_manager = nil
105
+ @flag_tracker = nil
88
106
 
89
107
  start_up(wait_for_sec)
90
108
  end
@@ -95,32 +113,22 @@ module LaunchDarkly
95
113
 
96
114
  @hooks = Concurrent::Array.new(@config.hooks + plugin_hooks)
97
115
 
98
- @shared_executor = Concurrent::SingleThreadExecutor.new
99
-
100
- data_store_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger)
101
- store_sink = LaunchDarkly::Impl::DataStore::UpdateSink.new(data_store_broadcaster)
102
-
103
- # We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
104
- # some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
105
- # the feature store through the Config object, so we need to make a new Config that uses
106
- # the wrapped store.
107
- @store = Impl::FeatureStoreClientWrapper.new(@config.feature_store, store_sink, @config.logger)
108
- updated_config = @config.clone
109
- updated_config.instance_variable_set(:@feature_store, @store)
110
- @config = updated_config
111
-
112
- @data_store_status_provider = LaunchDarkly::Impl::DataStore::StatusProvider.new(@store, store_sink)
116
+ # Initialize the data system (FDv1 for now, will support FDv2 in the future)
117
+ # Note: FDv1 will update @config.feature_store to use its wrapped store
118
+ @data_system = Impl::DataSystem::FDv1.new(@sdk_key, @config)
113
119
 
120
+ # Components not managed by data system
114
121
  @big_segment_store_manager = Impl::BigSegmentStoreManager.new(@config.big_segments, @config.logger)
115
122
  @big_segment_store_status_provider = @big_segment_store_manager.status_provider
116
123
 
117
- get_flag = lambda { |key| @store.get(FEATURES, key) }
118
- get_segment = lambda { |key| @store.get(SEGMENTS, key) }
124
+ get_flag = lambda { |key| @data_system.store.get(Impl::DataStore::FEATURES, key) }
125
+ get_segment = lambda { |key| @data_system.store.get(Impl::DataStore::SEGMENTS, key) }
119
126
  get_big_segments_membership = lambda { |key| @big_segment_store_manager.get_context_membership(key) }
120
127
  @evaluator = LaunchDarkly::Impl::Evaluator.new(get_flag, get_segment, get_big_segments_membership, @config.logger)
121
128
 
122
129
  if !@config.offline? && @config.send_events && !@config.diagnostic_opt_out?
123
130
  diagnostic_accumulator = Impl::DiagnosticAccumulator.new(Impl::DiagnosticAccumulator.create_diagnostic_id(@sdk_key))
131
+ @data_system.set_diagnostic_accumulator(diagnostic_accumulator)
124
132
  else
125
133
  diagnostic_accumulator = nil
126
134
  end
@@ -131,38 +139,14 @@ module LaunchDarkly
131
139
  @event_processor = EventProcessor.new(@sdk_key, @config, nil, diagnostic_accumulator)
132
140
  end
133
141
 
134
- if @config.use_ldd?
135
- @config.logger.info { "[LDClient] Started LaunchDarkly Client in LDD mode" }
136
- @data_source = LaunchDarkly::Impl::DataSource::NullUpdateProcessor.new
137
- return # requestor and update processor are not used in this mode
138
- end
139
-
140
- flag_tracker_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger)
141
- @flag_tracker = LaunchDarkly::Impl::FlagTracker.new(flag_tracker_broadcaster, lambda { |key, context| variation(key, context, nil) })
142
-
143
- data_source_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger)
144
-
145
- # Make the update sink available on the config so that our data source factory can access the sink with a shared executor.
146
- @config.data_source_update_sink = LaunchDarkly::Impl::DataSource::UpdateSink.new(@store, data_source_broadcaster, flag_tracker_broadcaster)
147
-
148
- @data_source_status_provider = LaunchDarkly::Impl::DataSource::StatusProvider.new(data_source_broadcaster, @config.data_source_update_sink)
149
-
150
- data_source_or_factory = @config.data_source || self.method(:create_default_data_source)
151
- if data_source_or_factory.respond_to? :call
152
- # Currently, data source factories take two parameters unless they need to be aware of diagnostic_accumulator, in
153
- # which case they take three parameters. This will be changed in the future to use a less awkware mechanism.
154
- if data_source_or_factory.arity == 3
155
- @data_source = data_source_or_factory.call(@sdk_key, @config, diagnostic_accumulator)
156
- else
157
- @data_source = data_source_or_factory.call(@sdk_key, @config)
158
- end
159
- else
160
- @data_source = data_source_or_factory
161
- end
142
+ # Create the flag tracker using the broadcaster from the data system
143
+ eval_fn = lambda { |key, context| variation(key, context, nil) }
144
+ @flag_tracker = Impl::FlagTracker.new(@data_system.flag_change_broadcaster, eval_fn)
162
145
 
163
146
  register_plugins(environment_metadata)
164
147
 
165
- ready = @data_source.start
148
+ # Start the data system
149
+ ready = @data_system.start
166
150
 
167
151
  return unless wait_for_sec > 0
168
152
 
@@ -173,7 +157,7 @@ module LaunchDarkly
173
157
  ok = ready.wait(wait_for_sec)
174
158
  if !ok
175
159
  @config.logger.error { "[LDClient] Timeout encountered waiting for LaunchDarkly client initialization" }
176
- elsif !@data_source.initialized?
160
+ elsif !initialized?
177
161
  @config.logger.error { "[LDClient] LaunchDarkly client initialization failed" }
178
162
  end
179
163
  end
@@ -236,22 +220,6 @@ module LaunchDarkly
236
220
  @hooks.push(hook)
237
221
  end
238
222
 
239
- #
240
- # Tells the client that all pending analytics events should be delivered as soon as possible.
241
- #
242
- # When the LaunchDarkly client generates analytics events (from {#variation}, {#variation_detail},
243
- # {#identify}, or {#track}), they are queued on a worker thread. The event thread normally
244
- # sends all queued events to LaunchDarkly at regular intervals, controlled by the
245
- # {Config#flush_interval} option. Calling `flush` triggers a send without waiting for the
246
- # next interval.
247
- #
248
- # Flushing is asynchronous, so this method will return before it is complete. However, if you
249
- # call {#close}, events are guaranteed to be sent before that method returns.
250
- #
251
- def flush
252
- @event_processor.flush
253
- end
254
-
255
223
  #
256
224
  # Creates a hash string that can be used by the JavaScript SDK to identify a context.
257
225
  # For more information, see [Secure mode](https://docs.launchdarkly.com/sdk/features/secure-mode#ruby).
@@ -288,7 +256,7 @@ module LaunchDarkly
288
256
  # @return [Boolean] true if the client has been initialized
289
257
  #
290
258
  def initialized?
291
- @config.offline? || @config.use_ldd? || @data_source.initialized?
259
+ @data_system.data_availability == @data_system.target_availability
292
260
  end
293
261
 
294
262
  #
@@ -594,7 +562,7 @@ module LaunchDarkly
594
562
  return FeatureFlagsState.new(false) if @config.offline?
595
563
 
596
564
  unless initialized?
597
- if @store.initialized?
565
+ if @data_system.store.initialized?
598
566
  @config.logger.warn { "Called all_flags_state before client initialization; using last known values from data store" }
599
567
  else
600
568
  @config.logger.warn { "Called all_flags_state before client initialization. Data store not available; returning empty state" }
@@ -609,9 +577,9 @@ module LaunchDarkly
609
577
  end
610
578
 
611
579
  begin
612
- features = @store.all(FEATURES)
580
+ features = @data_system.store.all(Impl::DataStore::FEATURES)
613
581
  rescue => exn
614
- Util.log_exception(@config.logger, "Unable to read flags for all_flags_state", exn)
582
+ Impl::Util.log_exception(@config.logger, "Unable to read flags for all_flags_state", exn)
615
583
  return FeatureFlagsState.new(false)
616
584
  end
617
585
 
@@ -628,7 +596,7 @@ module LaunchDarkly
628
596
  detail = eval_result.detail
629
597
  rescue => exn
630
598
  detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
631
- Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
599
+ Impl::Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
632
600
  end
633
601
 
634
602
  requires_experiment_data = experiment?(f, detail.reason)
@@ -656,11 +624,9 @@ module LaunchDarkly
656
624
  # @return [void]
657
625
  def close
658
626
  @config.logger.info { "[LDClient] Closing LaunchDarkly client..." }
659
- @data_source.stop
627
+ @data_system.stop
660
628
  @event_processor.stop
661
629
  @big_segment_store_manager.stop
662
- @store.stop
663
- @shared_executor.shutdown
664
630
  end
665
631
 
666
632
  #
@@ -671,33 +637,6 @@ module LaunchDarkly
671
637
  #
672
638
  attr_reader :big_segment_store_status_provider
673
639
 
674
- #
675
- # Returns an interface for tracking the status of a persistent data store.
676
- #
677
- # The {LaunchDarkly::Interfaces::DataStore::StatusProvider} has methods for
678
- # checking whether the data store is (as far as the SDK knows) currently
679
- # operational, tracking changes in this status, and getting cache
680
- # statistics. These are only relevant for a persistent data store; if you
681
- # are using an in-memory data store, then this method will return a stub
682
- # object that provides no information.
683
- #
684
- # @return [LaunchDarkly::Interfaces::DataStore::StatusProvider]
685
- #
686
- attr_reader :data_store_status_provider
687
-
688
- #
689
- # Returns an interface for tracking the status of the data source.
690
- #
691
- # The data source is the mechanism that the SDK uses to get feature flag
692
- # configurations, such as a streaming connection (the default) or poll
693
- # requests. The {LaunchDarkly::Interfaces::DataSource::StatusProvider} has
694
- # methods for checking whether the data source is (as far as the SDK knows)
695
- # currently operational and tracking changes in this status.
696
- #
697
- # @return [LaunchDarkly::Interfaces::DataSource::StatusProvider]
698
- #
699
- attr_reader :data_source_status_provider
700
-
701
640
  #
702
641
  # Returns an interface for tracking changes in feature flag configurations.
703
642
  #
@@ -705,23 +644,8 @@ module LaunchDarkly
705
644
  # requesting notifications about feature flag changes using an event
706
645
  # listener model.
707
646
  #
708
- attr_reader :flag_tracker
709
-
710
- private
711
-
712
- def create_default_data_source(sdk_key, config, diagnostic_accumulator)
713
- if config.offline?
714
- return LaunchDarkly::Impl::DataSource::NullUpdateProcessor.new
715
- end
716
- raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
717
- if config.stream?
718
- StreamProcessor.new(sdk_key, config, diagnostic_accumulator)
719
- else
720
- config.logger.info { "Disabling streaming API" }
721
- config.logger.warn { "You should only disable the streaming API if instructed to do so by LaunchDarkly support" }
722
- requestor = Requestor.new(sdk_key, config)
723
- PollingProcessor.new(config, requestor)
724
- end
647
+ def flag_tracker
648
+ @flag_tracker
725
649
  end
726
650
 
727
651
  #
@@ -731,7 +655,7 @@ module LaunchDarkly
731
655
  #
732
656
  # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
733
657
  #
734
- def variation_with_flag(key, context, default)
658
+ private def variation_with_flag(key, context, default)
735
659
  evaluate_internal(key, context, default, false)
736
660
  end
737
661
 
@@ -743,7 +667,7 @@ module LaunchDarkly
743
667
  #
744
668
  # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
745
669
  #
746
- def evaluate_internal(key, context, default, with_reasons)
670
+ private def evaluate_internal(key, context, default, with_reasons)
747
671
  if @config.offline?
748
672
  return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default), nil, nil
749
673
  end
@@ -761,7 +685,7 @@ module LaunchDarkly
761
685
  end
762
686
 
763
687
  unless initialized?
764
- if @store.initialized?
688
+ if @data_system.store.initialized?
765
689
  @config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
766
690
  else
767
691
  @config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
@@ -772,7 +696,7 @@ module LaunchDarkly
772
696
  end
773
697
 
774
698
  begin
775
- feature = @store.get(FEATURES, key)
699
+ feature = @data_system.store.get(Impl::DataStore::FEATURES, key)
776
700
  rescue
777
701
  # Ignored
778
702
  end
@@ -798,7 +722,7 @@ module LaunchDarkly
798
722
  record_flag_eval(feature, context, detail, default, with_reasons)
799
723
  [detail, feature, nil]
800
724
  rescue => exn
801
- Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
725
+ Impl::Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
802
726
  detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
803
727
  record_flag_eval_error(feature, context, default, detail.reason, with_reasons)
804
728
  [detail, feature, exn.to_s]
@@ -1,5 +1,4 @@
1
- require "uri"
2
- require "http"
1
+ require "ldclient-rb/impl/util"
3
2
 
4
3
  module LaunchDarkly
5
4
  #
@@ -28,10 +27,11 @@ module LaunchDarkly
28
27
  #
29
28
  # @param error [String]
30
29
  # @param exception [Exception, nil]
30
+ # @param headers [Hash, nil]
31
31
  # @return [Result]
32
32
  #
33
- def self.fail(error, exception = nil)
34
- Result.new(nil, error, exception)
33
+ def self.fail(error, exception = nil, headers = nil)
34
+ Result.new(nil, error, exception, headers)
35
35
  end
36
36
 
37
37
  #
@@ -58,75 +58,16 @@ module LaunchDarkly
58
58
  #
59
59
  attr_reader :exception
60
60
 
61
- private def initialize(value, error = nil, exception = nil)
62
- @value = value
63
- @error = error
64
- @exception = exception
65
- end
66
- end
67
-
68
- # @private
69
- module Util
70
- #
71
- # Append the payload filter key query parameter to the provided URI.
72
61
  #
73
- # @param uri [String]
74
- # @param config [Config]
75
- # @return [String]
62
+ # @return [Hash] Optional headers associated with the result
76
63
  #
77
- def self.add_payload_filter_key(uri, config)
78
- return uri if config.payload_filter_key.nil?
79
-
80
- begin
81
- parsed = URI.parse(uri)
82
- new_query_params = URI.decode_www_form(String(parsed.query)) << ["filter", config.payload_filter_key]
83
- parsed.query = URI.encode_www_form(new_query_params)
84
- parsed.to_s
85
- rescue URI::InvalidURIError
86
- config.logger.warn { "[LDClient] URI could not be parsed. No filtering will be applied." }
87
- uri
88
- end
89
- end
64
+ attr_reader :headers
90
65
 
91
- def self.new_http_client(uri_s, config)
92
- http_client_options = {}
93
- if config.socket_factory
94
- http_client_options["socket_class"] = config.socket_factory
95
- end
96
- proxy = URI.parse(uri_s).find_proxy
97
- unless proxy.nil?
98
- http_client_options["proxy"] = {
99
- proxy_address: proxy.host,
100
- proxy_port: proxy.port,
101
- proxy_username: proxy.user,
102
- proxy_password: proxy.password,
103
- }
104
- end
105
- HTTP::Client.new(http_client_options)
106
- .timeout({
107
- read: config.read_timeout,
108
- connect: config.connect_timeout,
109
- })
110
- .persistent(uri_s)
111
- end
112
-
113
- def self.log_exception(logger, message, exc)
114
- logger.error { "[LDClient] #{message}: #{exc.inspect}" }
115
- logger.debug { "[LDClient] Exception trace: #{exc.backtrace}" }
116
- end
117
-
118
- def self.http_error_recoverable?(status)
119
- if status >= 400 && status < 500
120
- status == 400 || status == 408 || status == 429
121
- else
122
- true
123
- end
124
- end
125
-
126
- def self.http_error_message(status, context, recoverable_message)
127
- desc = (status == 401 || status == 403) ? " (invalid SDK key)" : ""
128
- message = Util.http_error_recoverable?(status) ? recoverable_message : "giving up permanently"
129
- "HTTP error #{status}#{desc} for #{context} - #{message}"
66
+ private def initialize(value, error = nil, exception = nil, headers = nil)
67
+ @value = value
68
+ @error = error
69
+ @exception = exception
70
+ @headers = headers || {}
130
71
  end
131
72
  end
132
73
  end
@@ -1,3 +1,3 @@
1
1
  module LaunchDarkly
2
- VERSION = "8.11.2" # x-release-please-version
2
+ VERSION = "8.11.3" # x-release-please-version
3
3
  end
data/lib/ldclient-rb.rb CHANGED
@@ -5,23 +5,14 @@
5
5
  module LaunchDarkly
6
6
  end
7
7
 
8
- require "ldclient-rb/version"
9
- require "ldclient-rb/interfaces"
10
- require "ldclient-rb/util"
11
- require "ldclient-rb/flags_state"
12
- require "ldclient-rb/migrations"
13
- require "ldclient-rb/ldclient"
14
- require "ldclient-rb/cache_store"
15
- require "ldclient-rb/expiring_cache"
16
- require "ldclient-rb/memoized_value"
17
- require "ldclient-rb/in_memory_store"
8
+ # Public APIs - these define the main interfaces users interact with
18
9
  require "ldclient-rb/config"
19
10
  require "ldclient-rb/context"
20
- require "ldclient-rb/reference"
21
- require "ldclient-rb/stream"
22
- require "ldclient-rb/polling"
23
- require "ldclient-rb/simple_lru_cache"
24
- require "ldclient-rb/non_blocking_thread_pool"
25
- require "ldclient-rb/events"
26
- require "ldclient-rb/requestor"
11
+ require "ldclient-rb/flags_state"
27
12
  require "ldclient-rb/integrations"
13
+ require "ldclient-rb/interfaces"
14
+ require "ldclient-rb/ldclient"
15
+ require "ldclient-rb/migrations"
16
+ require "ldclient-rb/reference"
17
+ require "ldclient-rb/util"
18
+ require "ldclient-rb/version"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: launchdarkly-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.11.2
4
+ version: 8.11.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-05 00:00:00.000000000 Z
11
+ date: 2026-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -298,22 +298,22 @@ dependencies:
298
298
  name: openssl
299
299
  requirement: !ruby/object:Gem::Requirement
300
300
  requirements:
301
- - - "~>"
302
- - !ruby/object:Gem::Version
303
- version: '3.1'
304
301
  - - ">="
305
302
  - !ruby/object:Gem::Version
306
303
  version: 3.1.2
304
+ - - "<"
305
+ - !ruby/object:Gem::Version
306
+ version: '5.0'
307
307
  type: :runtime
308
308
  prerelease: false
309
309
  version_requirements: !ruby/object:Gem::Requirement
310
310
  requirements:
311
- - - "~>"
312
- - !ruby/object:Gem::Version
313
- version: '3.1'
314
311
  - - ">="
315
312
  - !ruby/object:Gem::Version
316
313
  version: 3.1.2
314
+ - - "<"
315
+ - !ruby/object:Gem::Version
316
+ version: '5.0'
317
317
  - !ruby/object:Gem::Dependency
318
318
  name: semantic
319
319
  requirement: !ruby/object:Gem::Requirement
@@ -387,23 +387,35 @@ files:
387
387
  - README.md
388
388
  - lib/launchdarkly-server-sdk.rb
389
389
  - lib/ldclient-rb.rb
390
- - lib/ldclient-rb/cache_store.rb
391
390
  - lib/ldclient-rb/config.rb
392
391
  - lib/ldclient-rb/context.rb
392
+ - lib/ldclient-rb/data_system.rb
393
393
  - lib/ldclient-rb/evaluation_detail.rb
394
394
  - lib/ldclient-rb/events.rb
395
- - lib/ldclient-rb/expiring_cache.rb
396
395
  - lib/ldclient-rb/flags_state.rb
397
396
  - lib/ldclient-rb/impl.rb
398
397
  - lib/ldclient-rb/impl/big_segments.rb
399
398
  - lib/ldclient-rb/impl/broadcaster.rb
399
+ - lib/ldclient-rb/impl/cache_store.rb
400
400
  - lib/ldclient-rb/impl/context.rb
401
401
  - lib/ldclient-rb/impl/context_filter.rb
402
402
  - lib/ldclient-rb/impl/data_source.rb
403
403
  - lib/ldclient-rb/impl/data_source/null_processor.rb
404
+ - lib/ldclient-rb/impl/data_source/polling.rb
405
+ - lib/ldclient-rb/impl/data_source/requestor.rb
406
+ - lib/ldclient-rb/impl/data_source/status_provider.rb
407
+ - lib/ldclient-rb/impl/data_source/stream.rb
404
408
  - lib/ldclient-rb/impl/data_store.rb
409
+ - lib/ldclient-rb/impl/data_store/data_kind.rb
410
+ - lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb
411
+ - lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb
412
+ - lib/ldclient-rb/impl/data_store/status_provider.rb
413
+ - lib/ldclient-rb/impl/data_store/store.rb
405
414
  - lib/ldclient-rb/impl/data_system.rb
406
415
  - lib/ldclient-rb/impl/data_system/fdv1.rb
416
+ - lib/ldclient-rb/impl/data_system/fdv2.rb
417
+ - lib/ldclient-rb/impl/data_system/polling.rb
418
+ - lib/ldclient-rb/impl/data_system/protocolv2.rb
407
419
  - lib/ldclient-rb/impl/dependency_tracker.rb
408
420
  - lib/ldclient-rb/impl/diagnostic_events.rb
409
421
  - lib/ldclient-rb/impl/evaluation_with_hook_result.rb
@@ -414,12 +426,15 @@ files:
414
426
  - lib/ldclient-rb/impl/event_sender.rb
415
427
  - lib/ldclient-rb/impl/event_summarizer.rb
416
428
  - lib/ldclient-rb/impl/event_types.rb
429
+ - lib/ldclient-rb/impl/expiring_cache.rb
417
430
  - lib/ldclient-rb/impl/flag_tracker.rb
418
431
  - lib/ldclient-rb/impl/integrations/consul_impl.rb
419
432
  - lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
420
433
  - lib/ldclient-rb/impl/integrations/file_data_source.rb
421
434
  - lib/ldclient-rb/impl/integrations/redis_impl.rb
422
435
  - lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb
436
+ - lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb
437
+ - lib/ldclient-rb/impl/memoized_value.rb
423
438
  - lib/ldclient-rb/impl/migrations/migrator.rb
424
439
  - lib/ldclient-rb/impl/migrations/tracker.rb
425
440
  - lib/ldclient-rb/impl/model/clause.rb
@@ -427,8 +442,10 @@ files:
427
442
  - lib/ldclient-rb/impl/model/preprocessed_data.rb
428
443
  - lib/ldclient-rb/impl/model/segment.rb
429
444
  - lib/ldclient-rb/impl/model/serialization.rb
445
+ - lib/ldclient-rb/impl/non_blocking_thread_pool.rb
430
446
  - lib/ldclient-rb/impl/repeating_task.rb
431
447
  - lib/ldclient-rb/impl/sampler.rb
448
+ - lib/ldclient-rb/impl/simple_lru_cache.rb
432
449
  - lib/ldclient-rb/impl/store_client_wrapper.rb
433
450
  - lib/ldclient-rb/impl/store_data_set_sorter.rb
434
451
  - lib/ldclient-rb/impl/unbounded_pool.rb
@@ -441,31 +458,32 @@ files:
441
458
  - lib/ldclient-rb/integrations/redis.rb
442
459
  - lib/ldclient-rb/integrations/test_data.rb
443
460
  - lib/ldclient-rb/integrations/test_data/flag_builder.rb
461
+ - lib/ldclient-rb/integrations/test_data_v2.rb
462
+ - lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb
444
463
  - lib/ldclient-rb/integrations/util/store_wrapper.rb
445
464
  - lib/ldclient-rb/interfaces.rb
446
465
  - lib/ldclient-rb/interfaces/big_segment_store.rb
447
466
  - lib/ldclient-rb/interfaces/data_source.rb
448
467
  - lib/ldclient-rb/interfaces/data_store.rb
468
+ - lib/ldclient-rb/interfaces/data_system.rb
449
469
  - lib/ldclient-rb/interfaces/feature_store.rb
450
470
  - lib/ldclient-rb/interfaces/flag_tracker.rb
451
471
  - lib/ldclient-rb/interfaces/hooks.rb
452
472
  - lib/ldclient-rb/interfaces/migrations.rb
453
473
  - lib/ldclient-rb/interfaces/plugins.rb
454
474
  - lib/ldclient-rb/ldclient.rb
455
- - lib/ldclient-rb/memoized_value.rb
456
475
  - lib/ldclient-rb/migrations.rb
457
- - lib/ldclient-rb/non_blocking_thread_pool.rb
458
- - lib/ldclient-rb/polling.rb
459
476
  - lib/ldclient-rb/reference.rb
460
- - lib/ldclient-rb/requestor.rb
461
- - lib/ldclient-rb/simple_lru_cache.rb
462
- - lib/ldclient-rb/stream.rb
463
477
  - lib/ldclient-rb/util.rb
464
478
  - lib/ldclient-rb/version.rb
465
479
  homepage: https://github.com/launchdarkly/ruby-server-sdk
466
480
  licenses:
467
481
  - Apache-2.0
468
- metadata: {}
482
+ metadata:
483
+ bug_tracker_uri: https://github.com/launchdarkly/ruby-server-sdk/issues
484
+ changelog_uri: https://github.com/launchdarkly/ruby-server-sdk/blob/main/CHANGELOG.md
485
+ homepage_uri: https://github.com/launchdarkly/ruby-server-sdk
486
+ source_code_uri: https://github.com/launchdarkly/ruby-server-sdk
469
487
  post_install_message:
470
488
  rdoc_options: []
471
489
  require_paths: