launchdarkly-server-sdk 7.3.2 → 8.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4a03c0847cb10ea287eaa193067eb3e74191ea124faad2ec6a632ccd04b3297
4
- data.tar.gz: dc06eb1a9d68e1fa972751ac4273ecc3284737279d1715b2769fde52f8457def
3
+ metadata.gz: bdb1eeb75ade6b2f82e1271c763f74ae577da09510e585b7cc1076ae9ff594d7
4
+ data.tar.gz: 8f794b216dff21125547319781efc2d77ce1632af6faccd8fa3c7fd96e2c8216
5
5
  SHA512:
6
- metadata.gz: 9496714bc7e4fe7ddfdf392255d89033a5d330b26397efd87ac166582d6ac74984843a91d5ee1236638f70310d8e4f03ab0d8fce0d0220381daed4b2bee9ea98
7
- data.tar.gz: ababf98f4a35776fcbd9911a06b2dca144fb5803a05ad9f1aabe5a5afcb776a9c76a9156059ccfc484888d32c1f3ef70a81b8060bb01bced991d72b417da0b5f
6
+ metadata.gz: 1ad1349bf430c6bb3a821d033f11106883c25d68aadc10bd622809b4c6dfe04e55ada3b9a49b8c92588389a4b3fb2112b1fd4fb63cdedf264cd3a0cc0bc0ac26
7
+ data.tar.gz: 6a605bd4f7ea9c7af3454e9a70bf5b3db1057456e5fc96249c807a706b0ca2fa8f0d3bd1238dae7e2fa5778ffe5d3b8728c9085499df0d952f76b3e41dcfa7a7
data/README.md CHANGED
@@ -3,7 +3,7 @@ LaunchDarkly Server-side SDK for Ruby
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/launchdarkly-server-sdk.svg)](http://badge.fury.io/rb/launchdarkly-server-sdk)
5
5
 
6
- [![Run CI](https://github.com/launchdarkly/ruby-server-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/launchdarkly/ruby-server-sdk/actions/workflows/ci.yml)
6
+ [![Circle CI](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/main.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/main)
7
7
  [![RubyDoc](https://img.shields.io/static/v1?label=docs+-+all+versions&message=reference&color=00add8)](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
8
8
  [![GitHub Pages](https://img.shields.io/static/v1?label=docs+-+latest&message=reference&color=00add8)](https://launchdarkly.github.io/ruby-server-sdk)
9
9
 
@@ -13,18 +13,6 @@ module LaunchDarkly
13
13
  #
14
14
  # Constructor for creating custom LaunchDarkly configurations.
15
15
  #
16
- # `user_keys_capacity` and `user_keys_flush_interval` are deprecated
17
- # configuration options. They exist to maintain backwards compatibility
18
- # with previous configurations. Newer code should prefer their replacement
19
- # options -- `context_keys_capacity` and `context_keys_flush_interval`.
20
- #
21
- # In the event both the user and context variations are provided, the
22
- # context specific configuration option will take precedence.
23
- #
24
- # Similarly, `private_attribute_names` is deprecated. Newer code should
25
- # prefer `private_attributes`. If both are provided, `private_attributes`
26
- # will take precedence.
27
- #
28
16
  # @param opts [Hash] the configuration options
29
17
  # @option opts [Logger] :logger See {#logger}.
30
18
  # @option opts [String] :base_uri ("https://sdk.launchdarkly.com") See {#base_uri}.
@@ -42,12 +30,9 @@ module LaunchDarkly
42
30
  # @option opts [Float] :poll_interval (30) See {#poll_interval}.
43
31
  # @option opts [Boolean] :stream (true) See {#stream?}.
44
32
  # @option opts [Boolean] all_attributes_private (false) See {#all_attributes_private}.
45
- # @option opts [Array] :private_attribute_names See {#private_attribute_names}.
46
33
  # @option opts [Array] :private_attributes See {#private_attributes}.
47
34
  # @option opts [Boolean] :send_events (true) See {#send_events}.
48
- # @option opts [Integer] :user_keys_capacity (1000) See {#user_keys_capacity}.
49
35
  # @option opts [Integer] :context_keys_capacity (1000) See {#context_keys_capacity}.
50
- # @option opts [Float] :user_keys_flush_interval (300) See {#user_keys_flush_interval}.
51
36
  # @option opts [Float] :context_keys_flush_interval (300) See {#context_keys_flush_interval}.
52
37
  # @option opts [Object] :data_source See {#data_source}.
53
38
  # @option opts [Boolean] :diagnostic_opt_out (false) See {#diagnostic_opt_out?}.
@@ -76,10 +61,10 @@ module LaunchDarkly
76
61
  @offline = opts.has_key?(:offline) ? opts[:offline] : Config.default_offline
77
62
  @poll_interval = opts.has_key?(:poll_interval) && opts[:poll_interval] > Config.default_poll_interval ? opts[:poll_interval] : Config.default_poll_interval
78
63
  @all_attributes_private = opts[:all_attributes_private] || false
79
- @private_attributes = opts[:private_attributes] || opts[:private_attribute_names] || []
64
+ @private_attributes = opts[:private_attributes] || []
80
65
  @send_events = opts.has_key?(:send_events) ? opts[:send_events] : Config.default_send_events
81
- @context_keys_capacity = opts[:context_keys_capacity] || opts[:user_keys_capacity] || Config.default_context_keys_capacity
82
- @context_keys_flush_interval = opts[:context_keys_flush_interval] || opts[:user_keys_flush_interval] || Config.default_user_keys_flush_interval
66
+ @context_keys_capacity = opts[:context_keys_capacity] || Config.default_context_keys_capacity
67
+ @context_keys_flush_interval = opts[:context_keys_flush_interval] || Config.default_context_keys_flush_interval
83
68
  @data_source = opts[:data_source]
84
69
  @diagnostic_opt_out = opts.has_key?(:diagnostic_opt_out) && opts[:diagnostic_opt_out]
85
70
  @diagnostic_recording_interval = opts.has_key?(:diagnostic_recording_interval) && opts[:diagnostic_recording_interval] > Config.minimum_diagnostic_recording_interval ?
@@ -258,14 +243,6 @@ module LaunchDarkly
258
243
  #
259
244
  attr_reader :private_attributes
260
245
 
261
- #
262
- # @deprecated Backwards compatibility alias for #private_attributes.
263
- #
264
- # @return [Integer]
265
- # @see #private_attributes
266
- #
267
- alias :private_attribute_names :private_attributes
268
-
269
246
  #
270
247
  # Whether to send events back to LaunchDarkly. This differs from {#offline?} in that it affects
271
248
  # only the sending of client-side events, not streaming or polling for events from the server.
@@ -281,14 +258,6 @@ module LaunchDarkly
281
258
  #
282
259
  attr_reader :context_keys_capacity
283
260
 
284
- #
285
- # @deprecated Backwards compatibility alias for #context_keys_capacity.
286
- #
287
- # @return [Integer]
288
- # @see #context_keys_flush_interval
289
- #
290
- alias :user_keys_capacity :context_keys_capacity
291
-
292
261
  #
293
262
  # The interval in seconds at which the event processor will reset its set of known context keys.
294
263
  # @return [Float]
@@ -296,14 +265,6 @@ module LaunchDarkly
296
265
  #
297
266
  attr_reader :context_keys_flush_interval
298
267
 
299
- #
300
- # @deprecated Backwards compatibility alias for #context_keys_flush_interval.
301
- #
302
- # @return [Integer]
303
- # @see #context_keys_flush_interval
304
- #
305
- alias :user_keys_flush_interval :context_keys_flush_interval
306
-
307
268
  #
308
269
  # An object that is responsible for receiving feature flag data from LaunchDarkly. By default,
309
270
  # the client uses its standard polling or streaming implementation; this is customizable for
@@ -570,18 +531,6 @@ module LaunchDarkly
570
531
  300
571
532
  end
572
533
 
573
- class << self
574
- #
575
- # @deprecated Backwards compatibility alias for #default_context_keys_capacity
576
- #
577
- alias :default_user_keys_capacity :default_context_keys_capacity
578
-
579
- #
580
- # @deprecated Backwards compatibility alias for #default_context_keys_flush_interval
581
- #
582
- alias :default_user_keys_flush_interval :default_context_keys_flush_interval
583
- end
584
-
585
534
  #
586
535
  # The default value for {#diagnostic_recording_interval}.
587
536
  # @return [Float] 900
@@ -647,25 +596,11 @@ module LaunchDarkly
647
596
  # @return [Integer]
648
597
  attr_reader :context_cache_size
649
598
 
650
- #
651
- # @deprecated Backwards compatibility alias for #context_cache_size
652
- #
653
- # @return [Integer]
654
- #
655
- alias :user_cache_size :context_cache_size
656
-
657
599
  # The maximum length of time (in seconds) that the Big Segment state for a context will be cached
658
600
  # by the SDK.
659
601
  # @return [Float]
660
602
  attr_reader :context_cache_time
661
603
 
662
- #
663
- # @deprecated Backwards compatibility alias for #context_cache_time
664
- #
665
- # @return [Float]
666
- #
667
- alias :user_cache_time :context_cache_time
668
-
669
604
  # The interval (in seconds) at which the SDK will poll the Big Segment store to make sure it is
670
605
  # available and to determine how long ago it was updated.
671
606
  # @return [Float]
@@ -324,7 +324,6 @@ module LaunchDarkly
324
324
  #
325
325
  def self.create(data)
326
326
  return create_invalid_context(ERR_NOT_HASH) unless data.is_a?(Hash)
327
- return create_legacy_context(data) unless data.has_key?(:kind)
328
327
 
329
328
  kind = data[:kind]
330
329
  if kind == KIND_MULTI
@@ -394,52 +393,6 @@ module LaunchDarkly
394
393
  new(nil, nil, nil, nil, false, nil, nil, error)
395
394
  end
396
395
 
397
- #
398
- # @param data [Hash]
399
- # @return [LDContext]
400
- #
401
- private_class_method def self.create_legacy_context(data)
402
- warn("DEPRECATED: legacy user format will be removed in 8.0.0", uplevel: 1)
403
-
404
- key = data[:key]
405
-
406
- # Legacy users are allowed to have "" as a key but they cannot have nil as a key.
407
- return create_invalid_context(ERR_KEY_EMPTY) if key.nil?
408
-
409
- name = data[:name]
410
- name_error = LaunchDarkly::Impl::Context.validate_name(name)
411
- return create_invalid_context(name_error) unless name_error.nil?
412
-
413
- anonymous = data[:anonymous]
414
- anonymous_error = LaunchDarkly::Impl::Context.validate_anonymous(anonymous, true)
415
- return create_invalid_context(anonymous_error) unless anonymous_error.nil?
416
-
417
- custom = data[:custom]
418
- unless custom.nil? || custom.is_a?(Hash)
419
- return create_invalid_context(ERR_CUSTOM_NON_HASH)
420
- end
421
-
422
- # We only need to create an attribute hash if one of these keys exist.
423
- # Everything else is stored in dedicated instance variables.
424
- attributes = custom.clone
425
- data.each do |k, v|
426
- case k
427
- when :ip, :email, :avatar, :firstName, :lastName, :country
428
- attributes ||= {}
429
- attributes[k] = v.clone
430
- else
431
- next
432
- end
433
- end
434
-
435
- private_attributes = data[:privateAttributeNames]
436
- if private_attributes && !private_attributes.is_a?(Array)
437
- return create_invalid_context(ERR_PRIVATE_NON_ARRAY)
438
- end
439
-
440
- new(key.to_s, key.to_s, KIND_DEFAULT, name, anonymous, attributes, private_attributes)
441
- end
442
-
443
396
  #
444
397
  # @param data [Hash]
445
398
  # @param kind [String]
@@ -1,4 +1,3 @@
1
-
2
1
  module LaunchDarkly
3
2
  # An object returned by {LDClient#variation_detail}, combining the result of a flag evaluation with
4
3
  # an explanation of how it was calculated.
@@ -13,6 +12,7 @@ module LaunchDarkly
13
12
  def initialize(value, variation_index, reason)
14
13
  raise ArgumentError.new("variation_index must be a number") if !variation_index.nil? && !(variation_index.is_a? Numeric)
15
14
  raise ArgumentError.new("reason must be an EvaluationReason") unless reason.is_a? EvaluationReason
15
+
16
16
  @value = value
17
17
  @variation_index = variation_index
18
18
  @reason = reason
@@ -100,6 +100,10 @@ module LaunchDarkly
100
100
  # a rule specified a nonexistent variation. An error message will always be logged in this case.
101
101
  ERROR_MALFORMED_FLAG = :MALFORMED_FLAG
102
102
 
103
+ # Value for {#error_kind} indicating that there was an inconsistency between the expected type of the flag, and the
104
+ # actual type of the variation evaluated.
105
+ ERROR_WRONG_TYPE = :WRONG_TYPE
106
+
103
107
  # Value for {#error_kind} indicating that the caller passed `nil` for the context parameter, or the
104
108
  # context was invalid.
105
109
  ERROR_USER_NOT_SPECIFIED = :USER_NOT_SPECIFIED
@@ -40,7 +40,9 @@ module LaunchDarkly
40
40
  default = nil,
41
41
  track_events = false,
42
42
  debug_until = nil,
43
- prereq_of = nil
43
+ prereq_of = nil,
44
+ sampling_ratio = nil,
45
+ exclude_from_summaries = false
44
46
  )
45
47
  end
46
48
 
@@ -55,6 +57,9 @@ module LaunchDarkly
55
57
  )
56
58
  end
57
59
 
60
+ def record_migration_op_event(event)
61
+ end
62
+
58
63
  def flush
59
64
  end
60
65
 
@@ -153,10 +158,12 @@ module LaunchDarkly
153
158
  default = nil,
154
159
  track_events = false,
155
160
  debug_until = nil,
156
- prereq_of = nil
161
+ prereq_of = nil,
162
+ sampling_ratio = nil,
163
+ exclude_from_summaries = false
157
164
  )
158
165
  post_to_inbox(LaunchDarkly::Impl::EvalEvent.new(timestamp, context, key, version, variation, value, reason,
159
- default, track_events, debug_until, prereq_of))
166
+ default, track_events, debug_until, prereq_of, sampling_ratio, exclude_from_summaries))
160
167
  end
161
168
 
162
169
  def record_identify_event(context)
@@ -167,6 +174,10 @@ module LaunchDarkly
167
174
  post_to_inbox(LaunchDarkly::Impl::CustomEvent.new(timestamp, context, key, data, metric_value))
168
175
  end
169
176
 
177
+ def record_migration_op_event(event)
178
+ post_to_inbox(event)
179
+ end
180
+
170
181
  def flush
171
182
  # flush is done asynchronously
172
183
  post_to_inbox(FlushMessage.new)
@@ -220,6 +231,7 @@ module LaunchDarkly
220
231
  @config = config
221
232
  @diagnostic_accumulator = config.diagnostic_opt_out? ? nil : diagnostic_accumulator
222
233
  @event_sender = event_sender
234
+ @sampler = LaunchDarkly::Impl::Sampler.new(Random.new)
223
235
 
224
236
  @context_keys = SimpleLRUCacheSet.new(config.context_keys_capacity)
225
237
  @formatter = EventOutputFormatter.new(config)
@@ -292,7 +304,7 @@ module LaunchDarkly
292
304
  return if @disabled.value
293
305
 
294
306
  # Always record the event in the summary.
295
- outbox.add_to_summary(event)
307
+ outbox.add_to_summary(event) unless event.exclude_from_summaries
296
308
 
297
309
  # Decide whether to add the event to the payload. Feature events may be added twice, once for
298
310
  # the event (if tracked) and once for debugging.
@@ -309,12 +321,12 @@ module LaunchDarkly
309
321
 
310
322
  # For each context we haven't seen before, we add an index event - unless this is already
311
323
  # an identify event for that context.
312
- if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent)
324
+ if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent) && !event.is_a?(LaunchDarkly::Impl::MigrationOpEvent)
313
325
  outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.context))
314
326
  end
315
327
 
316
- outbox.add_event(event) if will_add_full_event
317
- outbox.add_event(debug_event) unless debug_event.nil?
328
+ outbox.add_event(event) if will_add_full_event && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
329
+ outbox.add_event(debug_event) if !debug_event.nil? && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
318
330
  end
319
331
 
320
332
  #
@@ -443,6 +455,7 @@ module LaunchDarkly
443
455
  CUSTOM_KIND = 'custom'
444
456
  INDEX_KIND = 'index'
445
457
  DEBUG_KIND = 'debug'
458
+ MIGRATION_OP_KIND = 'migration_op'
446
459
  SUMMARY_KIND = 'summary'
447
460
 
448
461
  def initialize(config)
@@ -476,6 +489,64 @@ module LaunchDarkly
476
489
  out[:reason] = event.reason unless event.reason.nil?
477
490
  out
478
491
 
492
+ when LaunchDarkly::Impl::MigrationOpEvent
493
+ out = {
494
+ kind: MIGRATION_OP_KIND,
495
+ creationDate: event.timestamp,
496
+ contextKeys: event.context.keys,
497
+ operation: event.operation.to_s,
498
+ evaluation: {
499
+ key: event.key,
500
+ value: event.evaluation.value,
501
+ },
502
+ }
503
+
504
+ out[:evaluation][:version] = event.version unless event.version.nil?
505
+ out[:evaluation][:default] = event.default unless event.default.nil?
506
+ out[:evaluation][:variation] = event.evaluation.variation_index unless event.evaluation.variation_index.nil?
507
+ out[:evaluation][:reason] = event.evaluation.reason unless event.evaluation.reason.nil?
508
+ out[:samplingRatio] = event.sampling_ratio unless event.sampling_ratio.nil? || event.sampling_ratio == 1
509
+
510
+ measurements = []
511
+
512
+ unless event.invoked.empty?
513
+ measurements << {
514
+ "key": "invoked",
515
+ "values": event.invoked.map { |origin| [origin, true] }.to_h,
516
+ }
517
+ end
518
+
519
+ unless event.consistency_check.nil?
520
+ measurement = {
521
+ "key": "consistent",
522
+ "value": event.consistency_check,
523
+ }
524
+
525
+ unless event.consistency_check_ratio.nil? || event.consistency_check_ratio == 1
526
+ measurement[:samplingRatio] = event.consistency_check_ratio
527
+ end
528
+
529
+ measurements << measurement
530
+ end
531
+
532
+
533
+ unless event.latencies.empty?
534
+ measurements << {
535
+ "key": "latency_ms",
536
+ "values": event.latencies,
537
+ }
538
+ end
539
+
540
+ unless event.errors.empty?
541
+ measurements << {
542
+ "key": "error",
543
+ "values": event.errors.map { |origin| [origin, true] }.to_h,
544
+ }
545
+ end
546
+ out[:measurements] = measurements unless measurements.empty?
547
+
548
+ out
549
+
479
550
  when LaunchDarkly::Impl::IdentifyEvent
480
551
  {
481
552
  kind: IDENTIFY_KIND,
@@ -23,7 +23,7 @@ module LaunchDarkly
23
23
  @last_status = nil
24
24
 
25
25
  unless @store.nil?
26
- @cache = ExpiringCache.new(big_segments_config.user_cache_size, big_segments_config.user_cache_time)
26
+ @cache = ExpiringCache.new(big_segments_config.context_cache_size, big_segments_config.context_cache_time)
27
27
  @poll_worker = RepeatingTask.new(big_segments_config.status_poll_interval, 0, -> { poll_store_and_update_status }, logger)
28
28
  @poll_worker.start
29
29
  end
@@ -1,23 +1,33 @@
1
+ require 'set'
2
+
1
3
  module LaunchDarkly
2
4
  module Impl
3
5
  class Event
4
6
  # @param timestamp [Integer]
5
7
  # @param context [LaunchDarkly::LDContext]
6
- def initialize(timestamp, context)
8
+ # @param sampling_ratio [Integer, nil]
9
+ # @param exclude_from_summaries [Boolean]
10
+ def initialize(timestamp, context, sampling_ratio = nil, exclude_from_summaries = false)
7
11
  @timestamp = timestamp
8
12
  @context = context
13
+ @sampling_ratio = sampling_ratio
14
+ @exclude_from_summaries = exclude_from_summaries
9
15
  end
10
16
 
11
17
  # @return [Integer]
12
18
  attr_reader :timestamp
13
19
  # @return [LaunchDarkly::LDContext]
14
20
  attr_reader :context
21
+ # @return [Integer, nil]
22
+ attr_reader :sampling_ratio
23
+ # @return [Boolean]
24
+ attr_reader :exclude_from_summaries
15
25
  end
16
26
 
17
27
  class EvalEvent < Event
18
28
  def initialize(timestamp, context, key, version = nil, variation = nil, value = nil, reason = nil, default = nil,
19
- track_events = false, debug_until = nil, prereq_of = nil)
20
- super(timestamp, context)
29
+ track_events = false, debug_until = nil, prereq_of = nil, sampling_ratio = nil, exclude_from_summaries = false)
30
+ super(timestamp, context, sampling_ratio, exclude_from_summaries)
21
31
  @key = key
22
32
  @version = version
23
33
  @variation = variation
@@ -41,6 +51,54 @@ module LaunchDarkly
41
51
  attr_reader :prereq_of
42
52
  end
43
53
 
54
+ class MigrationOpEvent < Event
55
+ #
56
+ # A migration op event represents the results of a migration-assisted read or write operation.
57
+ #
58
+ # The event includes optional measurements reporting on consistency checks, error reporting, and operation latency
59
+ # values.
60
+ #
61
+ # @param timestamp [Integer]
62
+ # @param context [LaunchDarkly::LDContext]
63
+ # @param key [string]
64
+ # @param flag [LaunchDarkly::Impl::Model::FeatureFlag, nil]
65
+ # @param operation [Symbol]
66
+ # @param default_stage [Symbol]
67
+ # @param evaluation [LaunchDarkly::EvaluationDetail]
68
+ # @param invoked [Set]
69
+ # @param consistency_check [Boolean, nil]
70
+ # @param consistency_check_ratio [Integer, nil]
71
+ # @param errors [Set]
72
+ # @param latencies [Hash<Symbol, Float>]
73
+ #
74
+ def initialize(timestamp, context, key, flag, operation, default_stage, evaluation, invoked, consistency_check, consistency_check_ratio, errors, latencies)
75
+ super(timestamp, context)
76
+ @operation = operation
77
+ @key = key
78
+ @version = flag&.version
79
+ @sampling_ratio = flag&.sampling_ratio
80
+ @default = default_stage
81
+ @evaluation = evaluation
82
+ @consistency_check = consistency_check
83
+ @consistency_check_ratio = consistency_check.nil? ? nil : consistency_check_ratio
84
+ @invoked = invoked
85
+ @errors = errors
86
+ @latencies = latencies
87
+ end
88
+
89
+ attr_reader :operation
90
+ attr_reader :key
91
+ attr_reader :version
92
+ attr_reader :sampling_ratio
93
+ attr_reader :default
94
+ attr_reader :evaluation
95
+ attr_reader :consistency_check
96
+ attr_reader :consistency_check_ratio
97
+ attr_reader :invoked
98
+ attr_reader :errors
99
+ attr_reader :latencies
100
+ end
101
+
44
102
  class IdentifyEvent < Event
45
103
  def initialize(timestamp, context)
46
104
  super(timestamp, context)
@@ -112,7 +112,7 @@ module LaunchDarkly
112
112
  @pool = create_redis_pool(opts)
113
113
 
114
114
  # shutdown pool on close unless the client passed a custom pool and specified not to shutdown
115
- @pool_shutdown_on_close = !opts[:pool] || opts.fetch(:pool_shutdown_on_close, true)
115
+ @pool_shutdown_on_close = (!opts[:pool] || opts.fetch(:pool_shutdown_on_close, true))
116
116
 
117
117
  @prefix = opts[:prefix] || LaunchDarkly::Integrations::Redis::default_prefix
118
118
  @logger = opts[:logger] || Config.default_logger