launchdarkly-server-sdk 7.0.2 → 8.4.2

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 (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)