launchdarkly-server-sdk 7.0.2 → 8.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -4
  3. data/lib/ldclient-rb/config.rb +50 -70
  4. data/lib/ldclient-rb/context.rb +65 -50
  5. data/lib/ldclient-rb/evaluation_detail.rb +5 -1
  6. data/lib/ldclient-rb/events.rb +81 -8
  7. data/lib/ldclient-rb/impl/big_segments.rb +1 -1
  8. data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
  9. data/lib/ldclient-rb/impl/context.rb +3 -3
  10. data/lib/ldclient-rb/impl/context_filter.rb +30 -9
  11. data/lib/ldclient-rb/impl/data_source.rb +188 -0
  12. data/lib/ldclient-rb/impl/data_store.rb +59 -0
  13. data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
  14. data/lib/ldclient-rb/impl/evaluation_with_hook_result.rb +34 -0
  15. data/lib/ldclient-rb/impl/event_sender.rb +1 -0
  16. data/lib/ldclient-rb/impl/event_types.rb +61 -3
  17. data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
  18. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +12 -0
  19. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +8 -0
  20. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +16 -3
  21. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +19 -2
  22. data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
  23. data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
  24. data/lib/ldclient-rb/impl/model/feature_flag.rb +25 -3
  25. data/lib/ldclient-rb/impl/repeating_task.rb +2 -3
  26. data/lib/ldclient-rb/impl/sampler.rb +25 -0
  27. data/lib/ldclient-rb/impl/store_client_wrapper.rb +102 -8
  28. data/lib/ldclient-rb/in_memory_store.rb +7 -0
  29. data/lib/ldclient-rb/integrations/file_data.rb +1 -1
  30. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +84 -15
  31. data/lib/ldclient-rb/integrations/test_data.rb +3 -3
  32. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +11 -0
  33. data/lib/ldclient-rb/interfaces.rb +671 -0
  34. data/lib/ldclient-rb/ldclient.rb +313 -22
  35. data/lib/ldclient-rb/migrations.rb +230 -0
  36. data/lib/ldclient-rb/polling.rb +51 -5
  37. data/lib/ldclient-rb/reference.rb +11 -0
  38. data/lib/ldclient-rb/requestor.rb +5 -5
  39. data/lib/ldclient-rb/stream.rb +91 -29
  40. data/lib/ldclient-rb/util.rb +89 -0
  41. data/lib/ldclient-rb/version.rb +1 -1
  42. data/lib/ldclient-rb.rb +1 -0
  43. metadata +44 -6
@@ -1,9 +1,17 @@
1
1
  require "ldclient-rb/impl/big_segments"
2
+ require "ldclient-rb/impl/broadcaster"
3
+ require "ldclient-rb/impl/data_source"
4
+ require "ldclient-rb/impl/data_store"
2
5
  require "ldclient-rb/impl/diagnostic_events"
3
6
  require "ldclient-rb/impl/evaluator"
7
+ require "ldclient-rb/impl/evaluation_with_hook_result"
8
+ require "ldclient-rb/impl/flag_tracker"
4
9
  require "ldclient-rb/impl/store_client_wrapper"
10
+ require "ldclient-rb/impl/migrations/tracker"
11
+ require "concurrent"
5
12
  require "concurrent/atomics"
6
13
  require "digest/sha1"
14
+ require "forwardable"
7
15
  require "logger"
8
16
  require "benchmark"
9
17
  require "json"
@@ -16,6 +24,10 @@ module LaunchDarkly
16
24
  #
17
25
  class LDClient
18
26
  include Impl
27
+ extend Forwardable
28
+
29
+ def_delegators :@config, :logger
30
+
19
31
  #
20
32
  # Creates a new client instance that connects to LaunchDarkly. A custom
21
33
  # configuration parameter can also supplied to specify advanced options,
@@ -44,16 +56,24 @@ module LaunchDarkly
44
56
  end
45
57
 
46
58
  @sdk_key = sdk_key
59
+ @hooks = Concurrent::Array.new(config.hooks)
60
+
61
+ @shared_executor = Concurrent::SingleThreadExecutor.new
62
+
63
+ data_store_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, config.logger)
64
+ store_sink = LaunchDarkly::Impl::DataStore::UpdateSink.new(data_store_broadcaster)
47
65
 
48
66
  # We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
49
67
  # some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
50
68
  # the feature store through the Config object, so we need to make a new Config that uses
51
69
  # the wrapped store.
52
- @store = Impl::FeatureStoreClientWrapper.new(config.feature_store)
70
+ @store = Impl::FeatureStoreClientWrapper.new(config.feature_store, store_sink, config.logger)
53
71
  updated_config = config.clone
54
72
  updated_config.instance_variable_set(:@feature_store, @store)
55
73
  @config = updated_config
56
74
 
75
+ @data_store_status_provider = LaunchDarkly::Impl::DataStore::StatusProvider.new(@store, store_sink)
76
+
57
77
  @big_segment_store_manager = Impl::BigSegmentStoreManager.new(config.big_segments, @config.logger)
58
78
  @big_segment_store_status_provider = @big_segment_store_manager.status_provider
59
79
 
@@ -76,9 +96,20 @@ module LaunchDarkly
76
96
 
77
97
  if @config.use_ldd?
78
98
  @config.logger.info { "[LDClient] Started LaunchDarkly Client in LDD mode" }
99
+ @data_source = NullUpdateProcessor.new
79
100
  return # requestor and update processor are not used in this mode
80
101
  end
81
102
 
103
+ flag_tracker_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger)
104
+ @flag_tracker = LaunchDarkly::Impl::FlagTracker.new(flag_tracker_broadcaster, lambda { |key, context| variation(key, context, nil) })
105
+
106
+ data_source_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger)
107
+
108
+ # Make the update sink available on the config so that our data source factory can access the sink with a shared executor.
109
+ @config.data_source_update_sink = LaunchDarkly::Impl::DataSource::UpdateSink.new(@store, data_source_broadcaster, flag_tracker_broadcaster)
110
+
111
+ @data_source_status_provider = LaunchDarkly::Impl::DataSource::StatusProvider.new(data_source_broadcaster, @config.data_source_update_sink)
112
+
82
113
  data_source_or_factory = @config.data_source || self.method(:create_default_data_source)
83
114
  if data_source_or_factory.respond_to? :call
84
115
  # Currently, data source factories take two parameters unless they need to be aware of diagnostic_accumulator, in
@@ -103,6 +134,23 @@ module LaunchDarkly
103
134
  end
104
135
  end
105
136
 
137
+ #
138
+ # Add a hook to the client. In order to register a hook before the client starts, please use the `hooks` property of
139
+ # {#LDConfig}.
140
+ #
141
+ # Hooks provide entrypoints which allow for observation of SDK functions.
142
+ #
143
+ # @param hook [Interfaces::Hooks::Hook]
144
+ #
145
+ def add_hook(hook)
146
+ unless hook.is_a?(Interfaces::Hooks::Hook)
147
+ @config.logger.error { "[LDClient] Attempted to add a hook that does not include the LaunchDarkly::Intefaces::Hooks::Hook mixin. Ignoring." }
148
+ return
149
+ end
150
+
151
+ @hooks.push(hook)
152
+ end
153
+
106
154
  #
107
155
  # Tells the client that all pending analytics events should be delivered as soon as possible.
108
156
  #
@@ -127,7 +175,7 @@ module LaunchDarkly
127
175
  # @return [String, nil] a hash string or nil if the provided context was invalid
128
176
  #
129
177
  def secure_mode_hash(context)
130
- context = Impl::Context::make_context(context)
178
+ context = Impl::Context.make_context(context)
131
179
  unless context.valid?
132
180
  @config.logger.warn("secure_mode_hash called with invalid context: #{context.error}")
133
181
  return nil
@@ -167,10 +215,16 @@ module LaunchDarkly
167
215
  # @param default the default value of the flag; this is used if there is an error
168
216
  # condition making it impossible to find or evaluate the flag
169
217
  #
170
- # @return the variation for the provided context, or the default value if there's an an error
218
+ # @return the variation for the provided context, or the default value if there's an error
171
219
  #
172
220
  def variation(key, context, default)
173
- evaluate_internal(key, context, default, false).value
221
+ context = Impl::Context::make_context(context)
222
+ result = evaluate_with_hooks(key, context, default, :variation) do
223
+ detail, _, _ = variation_with_flag(key, context, default)
224
+ LaunchDarkly::Impl::EvaluationWithHookResult.new(detail)
225
+ end
226
+
227
+ result.evaluation_detail.value
174
228
  end
175
229
 
176
230
  #
@@ -197,7 +251,157 @@ module LaunchDarkly
197
251
  # @return [EvaluationDetail] an object describing the result
198
252
  #
199
253
  def variation_detail(key, context, default)
200
- evaluate_internal(key, context, default, true)
254
+ context = Impl::Context::make_context(context)
255
+ result = evaluate_with_hooks(key, context, default, :variation_detail) do
256
+ detail, _, _ = evaluate_internal(key, context, default, true)
257
+ LaunchDarkly::Impl::EvaluationWithHookResult.new(detail)
258
+ end
259
+
260
+ result.evaluation_detail
261
+ end
262
+
263
+ #
264
+ # evaluate_with_hook will run the provided block, wrapping it with evaluation hook support.
265
+ #
266
+ # Example:
267
+ #
268
+ # ```ruby
269
+ # evaluate_with_hooks(key, context, default, method) do
270
+ # puts 'This is being wrapped with evaluation hooks'
271
+ # end
272
+ # ```
273
+ #
274
+ # @param key [String]
275
+ # @param context [LDContext]
276
+ # @param default [any]
277
+ # @param method [Symbol]
278
+ # @param &block [#call] Implicit passed block
279
+ #
280
+ # @return [LaunchDarkly::Impl::EvaluationWithHookResult]
281
+ #
282
+ private def evaluate_with_hooks(key, context, default, method)
283
+ return yield if @hooks.empty?
284
+
285
+ hooks, evaluation_series_context = prepare_hooks(key, context, default, method)
286
+ hook_data = execute_before_evaluation(hooks, evaluation_series_context)
287
+ evaluation_result = yield
288
+ execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_result.evaluation_detail)
289
+
290
+ evaluation_result
291
+ end
292
+
293
+ #
294
+ # Execute the :before_evaluation stage of the evaluation series.
295
+ #
296
+ # This method will return the results of each hook, indexed into an array in the same order as the hooks. If a hook
297
+ # raised an uncaught exception, the value will be nil.
298
+ #
299
+ # @param hooks [Array<Interfaces::Hooks::Hook>]
300
+ # @param evaluation_series_context [EvaluationSeriesContext]
301
+ #
302
+ # @return [Array<any>]
303
+ #
304
+ private def execute_before_evaluation(hooks, evaluation_series_context)
305
+ hooks.map do |hook|
306
+ try_execute_stage(:before_evaluation, hook.metadata.name) do
307
+ hook.before_evaluation(evaluation_series_context, {})
308
+ end
309
+ end
310
+ end
311
+
312
+ #
313
+ # Execute the :after_evaluation stage of the evaluation series.
314
+ #
315
+ # This method will return the results of each hook, indexed into an array in the same order as the hooks. If a hook
316
+ # raised an uncaught exception, the value will be nil.
317
+ #
318
+ # @param hooks [Array<Interfaces::Hooks::Hook>]
319
+ # @param evaluation_series_context [EvaluationSeriesContext]
320
+ # @param hook_data [Array<any>]
321
+ # @param evaluation_detail [EvaluationDetail]
322
+ #
323
+ # @return [Array<any>]
324
+ #
325
+ private def execute_after_evaluation(hooks, evaluation_series_context, hook_data, evaluation_detail)
326
+ hooks.zip(hook_data).reverse.map do |(hook, data)|
327
+ try_execute_stage(:after_evaluation, hook.metadata.name) do
328
+ hook.after_evaluation(evaluation_series_context, data, evaluation_detail)
329
+ end
330
+ end
331
+ end
332
+
333
+ #
334
+ # Try to execute the provided block. If execution raises an exception, catch and log it, then move on with
335
+ # execution.
336
+ #
337
+ # @return [any]
338
+ #
339
+ private def try_execute_stage(method, hook_name)
340
+ begin
341
+ yield
342
+ rescue => e
343
+ @config.logger.error { "[LDClient] An error occurred in #{method} of the hook #{hook_name}: #{e}" }
344
+ nil
345
+ end
346
+ end
347
+
348
+ #
349
+ # Return a copy of the existing hooks and a few instance of the EvaluationSeriesContext used for the evaluation series.
350
+ #
351
+ # @param key [String]
352
+ # @param context [LDContext]
353
+ # @param default [any]
354
+ # @param method [Symbol]
355
+ # @return [Array[Array<Interfaces::Hooks::Hook>, Interfaces::Hooks::EvaluationSeriesContext]]
356
+ #
357
+ private def prepare_hooks(key, context, default, method)
358
+ # Copy the hooks to use a consistent set during the evaluation series.
359
+ #
360
+ # Hooks can be added and we want to ensure all correct stages for a given hook execute. For example, we do not
361
+ # want to trigger the after_evaluation method without also triggering the before_evaluation method.
362
+ hooks = @hooks.dup
363
+ evaluation_series_context = Interfaces::Hooks::EvaluationSeriesContext.new(key, context, default, method)
364
+
365
+ [hooks, evaluation_series_context]
366
+ end
367
+
368
+ #
369
+ # This method returns the migration stage of the migration feature flag for the given evaluation context.
370
+ #
371
+ # This method returns the default stage if there is an error or the flag does not exist. If the default stage is not
372
+ # a valid stage, then a default stage of 'off' will be used instead.
373
+ #
374
+ # @param key [String]
375
+ # @param context [LDContext]
376
+ # @param default_stage [Symbol]
377
+ #
378
+ # @return [Array<Symbol, Interfaces::Migrations::OpTracker>]
379
+ #
380
+ def migration_variation(key, context, default_stage)
381
+ unless Migrations::VALID_STAGES.include? default_stage
382
+ @config.logger.error { "[LDClient] default_stage #{default_stage} is not a valid stage; continuing with 'off' as default" }
383
+ default_stage = Migrations::STAGE_OFF
384
+ end
385
+
386
+ context = Impl::Context::make_context(context)
387
+ result = evaluate_with_hooks(key, context, default_stage, :migration_variation) do
388
+ detail, flag, _ = variation_with_flag(key, context, default_stage.to_s)
389
+
390
+ stage = detail.value
391
+ stage = stage.to_sym if stage.respond_to? :to_sym
392
+
393
+ if Migrations::VALID_STAGES.include?(stage)
394
+ tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
395
+ next LaunchDarkly::Impl::EvaluationWithHookResult.new(detail, {stage: stage, tracker: tracker})
396
+ end
397
+
398
+ detail = LaunchDarkly::Impl::Evaluator.error_result(LaunchDarkly::EvaluationReason::ERROR_WRONG_TYPE, default_stage.to_s)
399
+ tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
400
+
401
+ LaunchDarkly::Impl::EvaluationWithHookResult.new(detail, {stage: default_stage, tracker: tracker})
402
+ end
403
+
404
+ [result.results[:stage], result.results[:tracker]]
201
405
  end
202
406
 
203
407
  #
@@ -260,6 +464,32 @@ module LaunchDarkly
260
464
  @event_processor.record_custom_event(context, event_name, data, metric_value)
261
465
  end
262
466
 
467
+ #
468
+ # Tracks the results of a migrations operation. This event includes measurements which can be used to enhance the
469
+ # observability of a migration within the LaunchDarkly UI.
470
+ #
471
+ # This event should be generated through {Interfaces::Migrations::OpTracker}. If you are using the
472
+ # {Interfaces::Migrations::Migrator} to handle migrations, this event will be created and emitted
473
+ # automatically.
474
+ #
475
+ # @param tracker [LaunchDarkly::Interfaces::Migrations::OpTracker]
476
+ #
477
+ def track_migration_op(tracker)
478
+ unless tracker.is_a? LaunchDarkly::Interfaces::Migrations::OpTracker
479
+ @config.logger.error { "invalid op tracker received in track_migration_op" }
480
+ return
481
+ end
482
+
483
+ event = tracker.build
484
+ if event.is_a? String
485
+ @config.logger.error { "[LDClient] Error occurred generating migration op event; #{event}" }
486
+ return
487
+ end
488
+
489
+
490
+ @event_processor.record_migration_op_event(event)
491
+ end
492
+
263
493
  #
264
494
  # Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given context,
265
495
  # including the flag values and also metadata that can be used on the front end. This method does not
@@ -345,16 +575,53 @@ module LaunchDarkly
345
575
  @event_processor.stop
346
576
  @big_segment_store_manager.stop
347
577
  @store.stop
578
+ @shared_executor.shutdown
348
579
  end
349
580
 
350
581
  #
351
582
  # Returns an interface for tracking the status of a Big Segment store.
352
583
  #
353
- # The {BigSegmentStoreStatusProvider} has methods for checking whether the Big Segment store
584
+ # The {Interfaces::BigSegmentStoreStatusProvider} has methods for checking whether the Big Segment store
354
585
  # is (as far as the SDK knows) currently operational and tracking changes in this status.
355
586
  #
356
587
  attr_reader :big_segment_store_status_provider
357
588
 
589
+ #
590
+ # Returns an interface for tracking the status of a persistent data store.
591
+ #
592
+ # The {LaunchDarkly::Interfaces::DataStore::StatusProvider} has methods for
593
+ # checking whether the data store is (as far as the SDK knows) currently
594
+ # operational, tracking changes in this status, and getting cache
595
+ # statistics. These are only relevant for a persistent data store; if you
596
+ # are using an in-memory data store, then this method will return a stub
597
+ # object that provides no information.
598
+ #
599
+ # @return [LaunchDarkly::Interfaces::DataStore::StatusProvider]
600
+ #
601
+ attr_reader :data_store_status_provider
602
+
603
+ #
604
+ # Returns an interface for tracking the status of the data source.
605
+ #
606
+ # The data source is the mechanism that the SDK uses to get feature flag
607
+ # configurations, such as a streaming connection (the default) or poll
608
+ # requests. The {LaunchDarkly::Interfaces::DataSource::StatusProvider} has
609
+ # methods for checking whether the data source is (as far as the SDK knows)
610
+ # currently operational and tracking changes in this status.
611
+ #
612
+ # @return [LaunchDarkly::Interfaces::DataSource::StatusProvider]
613
+ #
614
+ attr_reader :data_source_status_provider
615
+
616
+ #
617
+ # Returns an interface for tracking changes in feature flag configurations.
618
+ #
619
+ # The {LaunchDarkly::Interfaces::FlagTracker} contains methods for
620
+ # requesting notifications about feature flag changes using an event
621
+ # listener model.
622
+ #
623
+ attr_reader :flag_tracker
624
+
358
625
  private
359
626
 
360
627
  def create_default_data_source(sdk_key, config, diagnostic_accumulator)
@@ -372,24 +639,40 @@ module LaunchDarkly
372
639
  end
373
640
  end
374
641
 
375
- # @param context [Hash, LDContext]
376
- # @return [EvaluationDetail]
642
+ #
643
+ # @param key [String]
644
+ # @param context [LDContext]
645
+ # @param default [Object]
646
+ #
647
+ # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
648
+ #
649
+ def variation_with_flag(key, context, default)
650
+ evaluate_internal(key, context, default, false)
651
+ end
652
+
653
+ #
654
+ # @param key [String]
655
+ # @param context [LDContext]
656
+ # @param default [Object]
657
+ # @param with_reasons [Boolean]
658
+ #
659
+ # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
660
+ #
377
661
  def evaluate_internal(key, context, default, with_reasons)
378
662
  if @config.offline?
379
- return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
663
+ return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default), nil, nil
380
664
  end
381
665
 
382
666
  if context.nil?
383
667
  @config.logger.error { "[LDClient] Must specify context" }
384
668
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
385
- return detail
669
+ return detail, nil, "no context provided"
386
670
  end
387
671
 
388
- context = Impl::Context::make_context(context)
389
672
  unless context.valid?
390
- @config.logger.error { "[LDClient] Context was invalid for flag evaluation (#{context.error}); returning default value" }
673
+ @config.logger.error { "[LDClient] Context was invalid for evaluation of flag '#{key}' (#{context.error}); returning default value" }
391
674
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
392
- return detail
675
+ return detail, nil, context.error
393
676
  end
394
677
 
395
678
  unless initialized?
@@ -399,17 +682,21 @@ module LaunchDarkly
399
682
  @config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
400
683
  detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
401
684
  record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
402
- return detail
685
+ return detail, nil, "client not initialized"
403
686
  end
404
687
  end
405
688
 
406
- feature = @store.get(FEATURES, key)
689
+ begin
690
+ feature = @store.get(FEATURES, key)
691
+ rescue
692
+ # Ignored
693
+ end
407
694
 
408
695
  if feature.nil?
409
696
  @config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
410
697
  detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
411
698
  record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
412
- return detail
699
+ return detail, nil, "feature flag not found"
413
700
  end
414
701
 
415
702
  begin
@@ -424,12 +711,12 @@ module LaunchDarkly
424
711
  detail = EvaluationDetail.new(default, nil, detail.reason)
425
712
  end
426
713
  record_flag_eval(feature, context, detail, default, with_reasons)
427
- detail
714
+ [detail, feature, nil]
428
715
  rescue => exn
429
716
  Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
430
717
  detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
431
718
  record_flag_eval_error(feature, context, default, detail.reason, with_reasons)
432
- detail
719
+ [detail, feature, exn.to_s]
433
720
  end
434
721
  end
435
722
 
@@ -445,7 +732,9 @@ module LaunchDarkly
445
732
  default,
446
733
  add_experiment_data || flag[:trackEvents] || false,
447
734
  flag[:debugEventsUntilDate],
448
- nil
735
+ nil,
736
+ flag[:samplingRatio],
737
+ !!flag[:excludeFromSummaries]
449
738
  )
450
739
  end
451
740
 
@@ -461,13 +750,15 @@ module LaunchDarkly
461
750
  nil,
462
751
  add_experiment_data || prereq_flag[:trackEvents] || false,
463
752
  prereq_flag[:debugEventsUntilDate],
464
- prereq_of_flag[:key]
753
+ prereq_of_flag[:key],
754
+ prereq_flag[:samplingRatio],
755
+ !!prereq_flag[:excludeFromSummaries]
465
756
  )
466
757
  end
467
758
 
468
759
  private def record_flag_eval_error(flag, context, default, reason, with_reasons)
469
760
  @event_processor.record_eval_event(context, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
470
- flag[:trackEvents], flag[:debugEventsUntilDate], nil)
761
+ flag[:trackEvents], flag[:debugEventsUntilDate], nil, flag[:samplingRatio], !!flag[:excludeFromSummaries])
471
762
  end
472
763
 
473
764
  #
@@ -479,7 +770,7 @@ module LaunchDarkly
479
770
  #
480
771
  private def record_unknown_flag_eval(flag_key, context, default, reason, with_reasons)
481
772
  @event_processor.record_eval_event(context, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
482
- false, nil, nil)
773
+ false, nil, nil, 1, false)
483
774
  end
484
775
 
485
776
  private def experiment?(flag, reason)