launchdarkly-server-sdk 8.15.1-java → 8.17.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ldclient-rb/config.rb +3 -0
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +5 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +26 -5
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +1 -1
- data/lib/ldclient-rb/ldclient.rb +73 -4
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ae14db1c781b4710d0d0f9487ee7eb0453c81cec33d48deff97ed189fdb864a
|
|
4
|
+
data.tar.gz: d8b6ed68295b5ca5bec082bfe6a638e9b2b840e5d72764ca3e53fd5f347a589e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 163471afe99cdb36fcba1a63cea9d7ba31cfddaa052684f16e8b0d5f847450fc4e127e88ca6d91244d68c42946d05205cf23b100dc8942ad2406004f2dfcb8c1
|
|
7
|
+
data.tar.gz: 39d3fb3d49c3506130b0cc26c8299867fa30e1c1bb946c8f961cec131b4b2f02f21d3c372bbfa6558ab26ade14ba12a9643a308434400b354b03e2d566eb2da2
|
data/lib/ldclient-rb/config.rb
CHANGED
|
@@ -362,6 +362,9 @@ module LaunchDarkly
|
|
|
362
362
|
# This payload filter key only applies to the default streaming and polling data sources. It will not affect TestData or FileData
|
|
363
363
|
# data sources, nor will it be applied to any data source provided through the {#data_source} config property.
|
|
364
364
|
#
|
|
365
|
+
# Payload filtering is not supported with the FDv2 data system, so this key has no effect on requests made by FDv2
|
|
366
|
+
# data sources.
|
|
367
|
+
#
|
|
365
368
|
attr_reader :payload_filter_key
|
|
366
369
|
|
|
367
370
|
#
|
|
@@ -54,6 +54,11 @@ module LaunchDarkly
|
|
|
54
54
|
@config = config
|
|
55
55
|
@data_system_config = data_system_config
|
|
56
56
|
@logger = config.logger
|
|
57
|
+
|
|
58
|
+
unless config.payload_filter_key.nil?
|
|
59
|
+
@logger.warn { "[LDClient] Payload filtering is not supported with the FDv2 data system; the configured payload filter has no effect on FDv2 requests" }
|
|
60
|
+
end
|
|
61
|
+
|
|
57
62
|
@synchronizer_builders = data_system_config.synchronizers || []
|
|
58
63
|
@fdv1_fallback_synchronizer_builder = data_system_config.fdv1_fallback_synchronizer
|
|
59
64
|
@disabled = @config.offline?
|
|
@@ -4,22 +4,39 @@ require "concurrent/atomics"
|
|
|
4
4
|
|
|
5
5
|
module LaunchDarkly
|
|
6
6
|
module Impl
|
|
7
|
+
#
|
|
8
|
+
# Runs a task again and again on a worker thread, with a fixed interval
|
|
9
|
+
# between runs.
|
|
10
|
+
#
|
|
11
|
+
# The worker waits on an event instead of calling `sleep`, so `stop` can
|
|
12
|
+
# wake it at once even if `stop` runs before the worker starts waiting.
|
|
13
|
+
#
|
|
14
|
+
# @private
|
|
15
|
+
#
|
|
7
16
|
class RepeatingTask
|
|
8
17
|
attr_reader :name
|
|
9
18
|
|
|
19
|
+
#
|
|
20
|
+
# @param interval [Numeric] seconds between the start of one run and the start of the next
|
|
21
|
+
# @param start_delay [Numeric, nil] seconds to wait before the first run
|
|
22
|
+
# @param task [Proc] the code to run
|
|
23
|
+
# @param logger [Logger]
|
|
24
|
+
# @param name [String] the name given to the worker thread
|
|
25
|
+
#
|
|
10
26
|
def initialize(interval, start_delay, task, logger, name)
|
|
11
27
|
@interval = interval
|
|
12
28
|
@start_delay = start_delay
|
|
13
29
|
@task = task
|
|
14
30
|
@logger = logger
|
|
15
31
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
|
32
|
+
@stop_event = Concurrent::Event.new
|
|
16
33
|
@worker = nil
|
|
17
34
|
@name = name
|
|
18
35
|
end
|
|
19
36
|
|
|
20
37
|
def start
|
|
21
38
|
@worker = Thread.new do
|
|
22
|
-
|
|
39
|
+
@stop_event.wait(@start_delay) unless @start_delay.nil? || @start_delay == 0
|
|
23
40
|
|
|
24
41
|
until @stopped.value do
|
|
25
42
|
started_at = Time.now
|
|
@@ -29,19 +46,23 @@ module LaunchDarkly
|
|
|
29
46
|
Impl::Util.log_exception(@logger, "Uncaught exception from repeating task", e)
|
|
30
47
|
end
|
|
31
48
|
delta = @interval - (Time.now - started_at)
|
|
32
|
-
if delta > 0
|
|
33
|
-
sleep(delta)
|
|
34
|
-
end
|
|
49
|
+
@stop_event.wait(delta) if delta > 0
|
|
35
50
|
end
|
|
36
51
|
end
|
|
37
52
|
|
|
38
53
|
@worker.name = @name
|
|
39
54
|
end
|
|
40
55
|
|
|
56
|
+
#
|
|
57
|
+
# Stops the worker thread and waits for it to finish.
|
|
58
|
+
#
|
|
59
|
+
# This method is safe to call more than once, before `start`, and from
|
|
60
|
+
# inside the task itself.
|
|
61
|
+
#
|
|
41
62
|
def stop
|
|
42
63
|
if @stopped.make_true
|
|
64
|
+
@stop_event.set
|
|
43
65
|
if @worker && @worker.alive? && @worker != Thread.current
|
|
44
|
-
@worker.run # causes the thread to wake up if it's currently in a sleep
|
|
45
66
|
@worker.join
|
|
46
67
|
end
|
|
47
68
|
end
|
|
@@ -487,7 +487,7 @@ module LaunchDarkly
|
|
|
487
487
|
#
|
|
488
488
|
class FlagRuleBuilder
|
|
489
489
|
# @api private
|
|
490
|
-
FlagRuleClause = Struct.new(:contextKind, :attribute, :op, :values, :negate, keyword_init: true) # rubocop:disable Naming/MethodName
|
|
490
|
+
FlagRuleClause = Struct.new(:contextKind, :attribute, :op, :values, :negate, keyword_init: true) # rubocop:disable Naming/MethodName
|
|
491
491
|
|
|
492
492
|
# @api private
|
|
493
493
|
def initialize(flag_builder)
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
|
@@ -33,10 +33,6 @@ module LaunchDarkly
|
|
|
33
33
|
|
|
34
34
|
def_delegators :@config, :logger
|
|
35
35
|
|
|
36
|
-
# @!method flush
|
|
37
|
-
# Delegates to {LaunchDarkly::EventProcessorMethods#flush}.
|
|
38
|
-
def_delegator :@event_processor, :flush
|
|
39
|
-
|
|
40
36
|
# @!method data_store_status_provider
|
|
41
37
|
# Delegates to the data system {LaunchDarkly::Impl::DataSystem#data_store_status_provider}.
|
|
42
38
|
# @return [LaunchDarkly::Interfaces::DataStore::StatusProvider]
|
|
@@ -79,6 +75,12 @@ module LaunchDarkly
|
|
|
79
75
|
config.instance_id = SecureRandom.uuid
|
|
80
76
|
@config = config
|
|
81
77
|
|
|
78
|
+
# The pid of the process that owns this client's background threads. A child process that
|
|
79
|
+
# inherits this client after a fork has a different pid. See #check_forked.
|
|
80
|
+
@owner_pid = Process.pid
|
|
81
|
+
# The pid of the process in which the fork warning was last logged, or nil.
|
|
82
|
+
@fork_warned_pid = Concurrent::AtomicReference.new(nil)
|
|
83
|
+
|
|
82
84
|
start_up(wait_for_sec)
|
|
83
85
|
end
|
|
84
86
|
|
|
@@ -97,6 +99,10 @@ module LaunchDarkly
|
|
|
97
99
|
# reason, it is recommended that any listener or hook integrations be added postfork unless you are certain it can
|
|
98
100
|
# survive the forking process.
|
|
99
101
|
#
|
|
102
|
+
# If the SDK is used in a forked child process before this method is called, it logs a warning once per process.
|
|
103
|
+
# The warning says that flag updates and analytics events will not work until `postfork` is called. Calling this
|
|
104
|
+
# method clears that condition for the current process.
|
|
105
|
+
#
|
|
100
106
|
# @param wait_for_sec [Float] maximum time (in seconds) to wait for initialization
|
|
101
107
|
#
|
|
102
108
|
def postfork(wait_for_sec = 5)
|
|
@@ -105,9 +111,51 @@ module LaunchDarkly
|
|
|
105
111
|
@big_segment_store_manager = nil
|
|
106
112
|
@flag_tracker = nil
|
|
107
113
|
|
|
114
|
+
@owner_pid = Process.pid
|
|
115
|
+
@fork_warned_pid.set(nil)
|
|
116
|
+
|
|
108
117
|
start_up(wait_for_sec)
|
|
109
118
|
end
|
|
110
119
|
|
|
120
|
+
FORK_WARNING_MESSAGE = "[LDClient] This process was forked after the LDClient was created. Background threads do not " +
|
|
121
|
+
"survive a fork, so flag updates and analytics events will not work in this process. Call LDClient#postfork after forking."
|
|
122
|
+
private_constant :FORK_WARNING_MESSAGE
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
# Log a warning once per process if this client is used in a process that was forked after the client was created.
|
|
126
|
+
#
|
|
127
|
+
# Ruby threads do not survive a fork. A child process that inherits a client has no stream thread, no event
|
|
128
|
+
# dispatcher thread, and no timer tasks, so flag updates and analytics events stop working. The SDK does not
|
|
129
|
+
# repair this by itself; the application must call {#postfork}.
|
|
130
|
+
#
|
|
131
|
+
# The check compares the current pid with the pid recorded in the constructor or in {#postfork}. It is a single
|
|
132
|
+
# `getpid` call, which is cheap compared to a flag evaluation. The "already warned" state is stored as the pid in
|
|
133
|
+
# which the warning was logged, not as a boolean. A boolean would be copied into every child on fork, so a
|
|
134
|
+
# grandchild process would inherit "already warned" from its parent and never get its own warning.
|
|
135
|
+
#
|
|
136
|
+
private def check_forked
|
|
137
|
+
pid = Process.pid
|
|
138
|
+
return if pid == @owner_pid
|
|
139
|
+
return unless fork_breaks_client?
|
|
140
|
+
|
|
141
|
+
if @fork_warned_pid.get_and_set(pid) != pid
|
|
142
|
+
@config.logger.warn { FORK_WARNING_MESSAGE }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
#
|
|
147
|
+
# Return true if this configuration needs background threads. Offline mode has none. LDD mode with events
|
|
148
|
+
# disabled reads flags from the persistent store and sends nothing, so a fork does not break it.
|
|
149
|
+
#
|
|
150
|
+
# @return [Boolean]
|
|
151
|
+
#
|
|
152
|
+
private def fork_breaks_client?
|
|
153
|
+
return false if @config.offline?
|
|
154
|
+
return false if @config.use_ldd? && !@config.send_events
|
|
155
|
+
|
|
156
|
+
true
|
|
157
|
+
end
|
|
158
|
+
|
|
111
159
|
private def start_up(wait_for_sec)
|
|
112
160
|
environment_metadata = get_environment_metadata
|
|
113
161
|
plugin_hooks = get_plugin_hooks(environment_metadata)
|
|
@@ -281,6 +329,7 @@ module LaunchDarkly
|
|
|
281
329
|
# @return the variation for the provided context, or the default value if there's an error
|
|
282
330
|
#
|
|
283
331
|
def variation(key, context, default)
|
|
332
|
+
check_forked
|
|
284
333
|
context = Impl::Context::make_context(context)
|
|
285
334
|
result = evaluate_with_hooks(key, context, default, :variation) do
|
|
286
335
|
detail, _, _ = variation_with_flag(key, context, default)
|
|
@@ -314,6 +363,7 @@ module LaunchDarkly
|
|
|
314
363
|
# @return [EvaluationDetail] an object describing the result
|
|
315
364
|
#
|
|
316
365
|
def variation_detail(key, context, default)
|
|
366
|
+
check_forked
|
|
317
367
|
context = Impl::Context::make_context(context)
|
|
318
368
|
result = evaluate_with_hooks(key, context, default, :variation_detail) do
|
|
319
369
|
detail, _, _ = evaluate_internal(key, context, default, true)
|
|
@@ -439,6 +489,7 @@ module LaunchDarkly
|
|
|
439
489
|
# @return [Array<Symbol, Interfaces::Migrations::OpTracker>]
|
|
440
490
|
#
|
|
441
491
|
def migration_variation(key, context, default_stage)
|
|
492
|
+
check_forked
|
|
442
493
|
unless Migrations::VALID_STAGES.include? default_stage
|
|
443
494
|
@config.logger.error { "[LDClient] default_stage #{default_stage} is not a valid stage; continuing with 'off' as default" }
|
|
444
495
|
default_stage = Migrations::STAGE_OFF
|
|
@@ -480,6 +531,7 @@ module LaunchDarkly
|
|
|
480
531
|
# @return [void]
|
|
481
532
|
#
|
|
482
533
|
def identify(context)
|
|
534
|
+
check_forked
|
|
483
535
|
context = LaunchDarkly::Impl::Context.make_context(context)
|
|
484
536
|
unless context.valid?
|
|
485
537
|
@config.logger.warn("Identify called with invalid context: #{context.error}")
|
|
@@ -516,6 +568,7 @@ module LaunchDarkly
|
|
|
516
568
|
# @return [void]
|
|
517
569
|
#
|
|
518
570
|
def track(event_name, context, data = nil, metric_value = nil)
|
|
571
|
+
check_forked
|
|
519
572
|
context = LaunchDarkly::Impl::Context.make_context(context)
|
|
520
573
|
unless context.valid?
|
|
521
574
|
@config.logger.warn("Track called with invalid context: #{context.error}")
|
|
@@ -536,6 +589,7 @@ module LaunchDarkly
|
|
|
536
589
|
# @param tracker [LaunchDarkly::Interfaces::Migrations::OpTracker]
|
|
537
590
|
#
|
|
538
591
|
def track_migration_op(tracker)
|
|
592
|
+
check_forked
|
|
539
593
|
unless tracker.is_a? LaunchDarkly::Interfaces::Migrations::OpTracker
|
|
540
594
|
@config.logger.error { "invalid op tracker received in track_migration_op" }
|
|
541
595
|
return
|
|
@@ -571,6 +625,8 @@ module LaunchDarkly
|
|
|
571
625
|
def all_flags_state(context, options={})
|
|
572
626
|
return FeatureFlagsState.new(false) if @config.offline?
|
|
573
627
|
|
|
628
|
+
check_forked
|
|
629
|
+
|
|
574
630
|
unless initialized?
|
|
575
631
|
if @data_system.store.initialized?
|
|
576
632
|
@config.logger.warn { "Called all_flags_state before client initialization; using last known values from data store" }
|
|
@@ -628,11 +684,24 @@ module LaunchDarkly
|
|
|
628
684
|
state
|
|
629
685
|
end
|
|
630
686
|
|
|
687
|
+
#
|
|
688
|
+
# Tells the client that all pending analytics events should be delivered as soon as possible.
|
|
689
|
+
#
|
|
690
|
+
# Delegates to {LaunchDarkly::EventProcessorMethods#flush}.
|
|
691
|
+
#
|
|
692
|
+
# @return [void]
|
|
693
|
+
#
|
|
694
|
+
def flush
|
|
695
|
+
check_forked
|
|
696
|
+
@event_processor.flush
|
|
697
|
+
end
|
|
698
|
+
|
|
631
699
|
#
|
|
632
700
|
# Releases all network connections and other resources held by the client, making it no longer usable.
|
|
633
701
|
#
|
|
634
702
|
# @return [void]
|
|
635
703
|
def close
|
|
704
|
+
check_forked
|
|
636
705
|
@config.logger.info { "[LDClient] Closing LaunchDarkly client..." }
|
|
637
706
|
@data_system.stop
|
|
638
707
|
@event_processor.stop
|
data/lib/ldclient-rb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: launchdarkly-server-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.
|
|
4
|
+
version: 8.17.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-09-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: aws-sdk-dynamodb
|