launchdarkly-server-sdk 8.13.0-java → 8.14.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/README.md +1 -1
- data/lib/ldclient-rb/config.rb +0 -3
- data/lib/ldclient-rb/data_system/polling_data_source_builder.rb +7 -5
- data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +12 -0
- data/lib/ldclient-rb/impl/data_store/store.rb +24 -0
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +67 -12
- data/lib/ldclient-rb/impl/data_system/polling.rb +99 -32
- data/lib/ldclient-rb/impl/data_system/protocolv2.rb +3 -29
- data/lib/ldclient-rb/impl/data_system/streaming.rb +54 -31
- data/lib/ldclient-rb/impl/data_system.rb +6 -5
- data/lib/ldclient-rb/impl/integrations/file_data_source_v2.rb +29 -22
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +4 -0
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +53 -48
- data/lib/ldclient-rb/integrations/consul.rb +6 -2
- data/lib/ldclient-rb/integrations/dynamodb.rb +6 -2
- data/lib/ldclient-rb/integrations/file_data.rb +0 -3
- data/lib/ldclient-rb/integrations/redis.rb +6 -2
- data/lib/ldclient-rb/integrations/test_data_v2.rb +0 -3
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +39 -19
- data/lib/ldclient-rb/interfaces/data_system.rb +60 -57
- data/lib/ldclient-rb/util.rb +3 -2
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +2 -2
|
@@ -72,6 +72,25 @@ module LaunchDarkly
|
|
|
72
72
|
|
|
73
73
|
change_set_builder = LaunchDarkly::Interfaces::DataSystem::ChangeSetBuilder.new
|
|
74
74
|
envid = nil
|
|
75
|
+
# The FDv1 Fallback Directive is one-way and terminal: once any
|
|
76
|
+
# connect handshake within this sync invocation carries it, the SDK
|
|
77
|
+
# is committed to engaging FDv1 as soon as the next full payload
|
|
78
|
+
# has been applied. We therefore latch this flag to true on first
|
|
79
|
+
# observation and never reset it -- a mid-sync reconnect whose
|
|
80
|
+
# response no longer carries the directive does NOT cancel a
|
|
81
|
+
# directive seen earlier. This matches the Go and Python SDK
|
|
82
|
+
# implementations, both of which use the same latch pattern.
|
|
83
|
+
#
|
|
84
|
+
# The flag has to bridge two callbacks: on_connect sees the response
|
|
85
|
+
# headers but on_event does not. A local closed over by both blocks
|
|
86
|
+
# is correct because:
|
|
87
|
+
# 1. Scope -- bound to a single sync invocation, so a future sync
|
|
88
|
+
# starts fresh. (Within this invocation, persistence across
|
|
89
|
+
# reconnects is the intended semantics, per above.)
|
|
90
|
+
# 2. Thread safety -- ld-eventsource dispatches on_connect,
|
|
91
|
+
# on_event, and on_error on the same SSE worker thread, so
|
|
92
|
+
# reads and writes here are single-threaded by construction.
|
|
93
|
+
fdv1_fallback_pending = false
|
|
75
94
|
|
|
76
95
|
base_uri = @http_config.base_uri + FDV2_STREAMING_ENDPOINT
|
|
77
96
|
headers = Impl::Util.default_http_headers(@sdk_key, @config)
|
|
@@ -85,30 +104,32 @@ module LaunchDarkly
|
|
|
85
104
|
|
|
86
105
|
@sse = SSE::Client.new(base_uri, **opts) do |client|
|
|
87
106
|
client.on_connect do |headers|
|
|
88
|
-
# Extract environment ID and check for fallback on successful connection
|
|
89
107
|
if headers
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if headers
|
|
94
|
-
log_connection_result(true)
|
|
95
|
-
yield LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
96
|
-
state: LaunchDarkly::Interfaces::DataSource::Status::OFF,
|
|
97
|
-
revert_to_fdv1: true,
|
|
98
|
-
environment_id: envid
|
|
99
|
-
)
|
|
100
|
-
stop
|
|
101
|
-
end
|
|
108
|
+
# Per-environment identifier: server sends it on every connect,
|
|
109
|
+
# but it never changes once known so only assign once.
|
|
110
|
+
envid ||= LaunchDarkly::Impl::DataSystem.lookup_header(headers, LD_ENVID_HEADER)
|
|
111
|
+
fdv1_fallback_pending = true if LaunchDarkly::Impl::DataSystem.fdv1_fallback_requested?(headers)
|
|
102
112
|
end
|
|
103
113
|
end
|
|
104
114
|
|
|
105
115
|
client.on_event do |event|
|
|
106
116
|
begin
|
|
107
|
-
update = process_message(event, change_set_builder, envid)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
update = process_message(event, change_set_builder, envid, fdv1_fallback_pending: fdv1_fallback_pending)
|
|
118
|
+
next unless update
|
|
119
|
+
|
|
120
|
+
log_connection_result(true)
|
|
121
|
+
@connection_attempt_start_time = 0
|
|
122
|
+
|
|
123
|
+
yield update
|
|
124
|
+
|
|
125
|
+
# When the FDv1 Fallback Directive rode along on a Valid update, close
|
|
126
|
+
# the stream so the primary synchronizer is stopped once the directive
|
|
127
|
+
# engages. process_message marks the Update with fallback_to_fdv1 only
|
|
128
|
+
# on payloads that complete a transfer, so the consumer has already
|
|
129
|
+
# applied the ChangeSet by the time we get here.
|
|
130
|
+
if update.fallback_to_fdv1
|
|
131
|
+
fdv1_fallback_pending = false
|
|
132
|
+
stop
|
|
112
133
|
end
|
|
113
134
|
rescue JSON::ParserError => e
|
|
114
135
|
@logger.info { "[LDClient] Error parsing stream event; will restart stream: #{e}" }
|
|
@@ -147,13 +168,9 @@ module LaunchDarkly
|
|
|
147
168
|
log_connection_result(false)
|
|
148
169
|
fallback = false
|
|
149
170
|
|
|
150
|
-
# Extract envid and fallback from error headers if available
|
|
151
171
|
if error.respond_to?(:headers) && error.headers
|
|
152
|
-
envid
|
|
153
|
-
|
|
154
|
-
if error.headers[LD_FD_FALLBACK_HEADER] == 'true'
|
|
155
|
-
fallback = true
|
|
156
|
-
end
|
|
172
|
+
envid ||= LaunchDarkly::Impl::DataSystem.lookup_header(error.headers, LD_ENVID_HEADER)
|
|
173
|
+
fallback = true if LaunchDarkly::Impl::DataSystem.fdv1_fallback_requested?(error.headers)
|
|
157
174
|
end
|
|
158
175
|
|
|
159
176
|
update = handle_error(error, envid, fallback)
|
|
@@ -193,9 +210,15 @@ module LaunchDarkly
|
|
|
193
210
|
# @param message [SSE::StreamEvent]
|
|
194
211
|
# @param change_set_builder [LaunchDarkly::Interfaces::DataSystem::ChangeSetBuilder]
|
|
195
212
|
# @param envid [String, nil]
|
|
213
|
+
# @param fdv1_fallback_pending [Boolean] true when the connect-time
|
|
214
|
+
# response headers carried the FDv1 Fallback Directive. When set,
|
|
215
|
+
# the next Update that completes a payload transfer (TRANSFER_NONE
|
|
216
|
+
# or PAYLOAD_TRANSFERRED) is marked with fallback_to_fdv1: true so
|
|
217
|
+
# the consumer can engage the FDv1 Fallback Synchronizer after
|
|
218
|
+
# applying the in-flight ChangeSet.
|
|
196
219
|
# @return [LaunchDarkly::Interfaces::DataSystem::Update, nil]
|
|
197
220
|
#
|
|
198
|
-
private def process_message(message, change_set_builder, envid)
|
|
221
|
+
private def process_message(message, change_set_builder, envid, fdv1_fallback_pending: false)
|
|
199
222
|
event_type = message.type
|
|
200
223
|
|
|
201
224
|
# Handle heartbeat
|
|
@@ -214,7 +237,8 @@ module LaunchDarkly
|
|
|
214
237
|
change_set_builder.expect_changes
|
|
215
238
|
return LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
216
239
|
state: LaunchDarkly::Interfaces::DataSource::Status::VALID,
|
|
217
|
-
environment_id: envid
|
|
240
|
+
environment_id: envid,
|
|
241
|
+
fallback_to_fdv1: fdv1_fallback_pending
|
|
218
242
|
)
|
|
219
243
|
end
|
|
220
244
|
nil
|
|
@@ -231,9 +255,7 @@ module LaunchDarkly
|
|
|
231
255
|
|
|
232
256
|
when LaunchDarkly::Interfaces::DataSystem::EventName::GOODBYE
|
|
233
257
|
goodbye = LaunchDarkly::Impl::DataSystem::ProtocolV2::Goodbye.from_h(JSON.parse(message.data, symbolize_names: true))
|
|
234
|
-
|
|
235
|
-
@logger.error { "[LDClient] SSE server received error: #{goodbye.reason} (catastrophe: #{goodbye.catastrophe})" }
|
|
236
|
-
end
|
|
258
|
+
@logger.info { "[LDClient] SSE server received goodbye: #{goodbye.reason}" }
|
|
237
259
|
nil
|
|
238
260
|
|
|
239
261
|
when LaunchDarkly::Interfaces::DataSystem::EventName::ERROR
|
|
@@ -251,7 +273,8 @@ module LaunchDarkly
|
|
|
251
273
|
LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
252
274
|
state: LaunchDarkly::Interfaces::DataSource::Status::VALID,
|
|
253
275
|
change_set: change_set,
|
|
254
|
-
environment_id: envid
|
|
276
|
+
environment_id: envid,
|
|
277
|
+
fallback_to_fdv1: fdv1_fallback_pending
|
|
255
278
|
)
|
|
256
279
|
|
|
257
280
|
else
|
|
@@ -286,7 +309,7 @@ module LaunchDarkly
|
|
|
286
309
|
update = LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
287
310
|
state: LaunchDarkly::Interfaces::DataSource::Status::OFF,
|
|
288
311
|
error: error_info,
|
|
289
|
-
|
|
312
|
+
fallback_to_fdv1: true,
|
|
290
313
|
environment_id: envid
|
|
291
314
|
)
|
|
292
315
|
stop
|
|
@@ -247,8 +247,9 @@ module LaunchDarkly
|
|
|
247
247
|
# @return [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] Error information if applicable
|
|
248
248
|
attr_reader :error
|
|
249
249
|
|
|
250
|
-
# @return [Boolean] Whether
|
|
251
|
-
|
|
250
|
+
# @return [Boolean] Whether the LaunchDarkly server has instructed the SDK to
|
|
251
|
+
# fall back to the FDv1 protocol.
|
|
252
|
+
attr_reader :fallback_to_fdv1
|
|
252
253
|
|
|
253
254
|
# @return [String, nil] The environment ID if available
|
|
254
255
|
attr_reader :environment_id
|
|
@@ -257,14 +258,14 @@ module LaunchDarkly
|
|
|
257
258
|
# @param state [Symbol] The state of the data source
|
|
258
259
|
# @param change_set [ChangeSet, nil] The change set if available
|
|
259
260
|
# @param error [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] Error information if applicable
|
|
260
|
-
# @param
|
|
261
|
+
# @param fallback_to_fdv1 [Boolean] Whether to fall back to FDv1
|
|
261
262
|
# @param environment_id [String, nil] The environment ID if available
|
|
262
263
|
#
|
|
263
|
-
def initialize(state:, change_set: nil, error: nil,
|
|
264
|
+
def initialize(state:, change_set: nil, error: nil, fallback_to_fdv1: false, environment_id: nil)
|
|
264
265
|
@state = state
|
|
265
266
|
@change_set = change_set
|
|
266
267
|
@error = error
|
|
267
|
-
@
|
|
268
|
+
@fallback_to_fdv1 = fallback_to_fdv1
|
|
268
269
|
@environment_id = environment_id
|
|
269
270
|
end
|
|
270
271
|
end
|
|
@@ -71,31 +71,38 @@ module LaunchDarkly
|
|
|
71
71
|
# Implementation of the Initializer.fetch method.
|
|
72
72
|
#
|
|
73
73
|
# Reads all configured files once and returns their contents as a Basis.
|
|
74
|
+
# File-based data sources never request the FDv1 Fallback Directive,
|
|
75
|
+
# so the returned {FetchResult} always reports `fallback_to_fdv1: false`.
|
|
74
76
|
#
|
|
75
77
|
# @param selector_store [LaunchDarkly::Interfaces::DataSystem::SelectorStore] Provides the Selector (unused for file data)
|
|
76
|
-
# @return [LaunchDarkly::
|
|
78
|
+
# @return [LaunchDarkly::Interfaces::DataSystem::FetchResult]
|
|
77
79
|
#
|
|
78
80
|
def fetch(selector_store)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
result =
|
|
82
|
+
begin
|
|
83
|
+
@lock.synchronize do
|
|
84
|
+
if @closed
|
|
85
|
+
next LaunchDarkly::Result.fail('FileDataV2 source has been closed')
|
|
86
|
+
end
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
load_result = load_all_to_changeset
|
|
89
|
+
next load_result unless load_result.success?
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
change_set = load_result.value
|
|
92
|
+
basis = LaunchDarkly::Interfaces::DataSystem::Basis.new(
|
|
93
|
+
change_set: change_set,
|
|
94
|
+
persist: false,
|
|
95
|
+
environment_id: nil
|
|
96
|
+
)
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
LaunchDarkly::Result.success(basis)
|
|
99
|
+
end
|
|
100
|
+
rescue => e
|
|
101
|
+
@logger.error { "[LDClient] Error fetching file data: #{e.message}" }
|
|
102
|
+
LaunchDarkly::Result.fail("Error fetching file data: #{e.message}", e)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
LaunchDarkly::Interfaces::DataSystem::FetchResult.new(result: result, fallback_to_fdv1: false)
|
|
99
106
|
end
|
|
100
107
|
|
|
101
108
|
#
|
|
@@ -110,14 +117,14 @@ module LaunchDarkly
|
|
|
110
117
|
#
|
|
111
118
|
def sync(selector_store)
|
|
112
119
|
# First yield initial data
|
|
113
|
-
|
|
114
|
-
unless
|
|
120
|
+
initial_fetch = fetch(selector_store)
|
|
121
|
+
unless initial_fetch.success?
|
|
115
122
|
yield LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
116
123
|
state: LaunchDarkly::Interfaces::DataSource::Status::OFF,
|
|
117
124
|
error: LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
118
125
|
LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA,
|
|
119
126
|
0,
|
|
120
|
-
|
|
127
|
+
initial_fetch.error,
|
|
121
128
|
Time.now
|
|
122
129
|
)
|
|
123
130
|
)
|
|
@@ -126,7 +133,7 @@ module LaunchDarkly
|
|
|
126
133
|
|
|
127
134
|
yield LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
128
135
|
state: LaunchDarkly::Interfaces::DataSource::Status::VALID,
|
|
129
|
-
change_set:
|
|
136
|
+
change_set: initial_fetch.value.change_set
|
|
130
137
|
)
|
|
131
138
|
|
|
132
139
|
# Start watching for file changes
|
|
@@ -46,56 +46,61 @@ module LaunchDarkly
|
|
|
46
46
|
# Implementation of the Initializer.fetch method.
|
|
47
47
|
#
|
|
48
48
|
# Returns the current test data as a Basis for initial data loading.
|
|
49
|
+
# Test data sources never request the FDv1 Fallback Directive, so the
|
|
50
|
+
# returned {FetchResult} always reports `fallback_to_fdv1: false`.
|
|
49
51
|
#
|
|
50
52
|
# @param selector_store [LaunchDarkly::Interfaces::DataSystem::SelectorStore] Provides the Selector (unused for test data)
|
|
51
|
-
# @return [LaunchDarkly::
|
|
53
|
+
# @return [LaunchDarkly::Interfaces::DataSystem::FetchResult]
|
|
52
54
|
#
|
|
53
55
|
def fetch(selector_store)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
56
|
+
result =
|
|
57
|
+
begin
|
|
58
|
+
@lock.synchronize do
|
|
59
|
+
if @closed
|
|
60
|
+
next LaunchDarkly::Result.fail('TestDataV2 source has been closed')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Get all current flags and segments from test data
|
|
64
|
+
init_data = @test_data.make_init_data
|
|
65
|
+
version = @test_data.get_version
|
|
66
|
+
|
|
67
|
+
# Build a full transfer changeset
|
|
68
|
+
builder = LaunchDarkly::Interfaces::DataSystem::ChangeSetBuilder.new
|
|
69
|
+
builder.start(LaunchDarkly::Interfaces::DataSystem::IntentCode::TRANSFER_FULL)
|
|
70
|
+
|
|
71
|
+
# Add all flags to the changeset
|
|
72
|
+
init_data[:flags].each do |key, flag_data|
|
|
73
|
+
builder.add_put(
|
|
74
|
+
LaunchDarkly::Interfaces::DataSystem::ObjectKind::FLAG,
|
|
75
|
+
key,
|
|
76
|
+
flag_data[:version] || 1,
|
|
77
|
+
flag_data
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Add all segments to the changeset
|
|
82
|
+
init_data[:segments].each do |key, segment_data|
|
|
83
|
+
builder.add_put(
|
|
84
|
+
LaunchDarkly::Interfaces::DataSystem::ObjectKind::SEGMENT,
|
|
85
|
+
key,
|
|
86
|
+
segment_data[:version] || 1,
|
|
87
|
+
segment_data
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Create selector for this version
|
|
92
|
+
selector = LaunchDarkly::Interfaces::DataSystem::Selector.new_selector(version.to_s, version)
|
|
93
|
+
change_set = builder.finish(selector)
|
|
94
|
+
|
|
95
|
+
basis = LaunchDarkly::Interfaces::DataSystem::Basis.new(change_set: change_set, persist: false, environment_id: nil)
|
|
96
|
+
|
|
97
|
+
LaunchDarkly::Result.success(basis)
|
|
86
98
|
end
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
selector = LaunchDarkly::Interfaces::DataSystem::Selector.new_selector(version.to_s, version)
|
|
90
|
-
change_set = builder.finish(selector)
|
|
91
|
-
|
|
92
|
-
basis = LaunchDarkly::Interfaces::DataSystem::Basis.new(change_set: change_set, persist: false, environment_id: nil)
|
|
93
|
-
|
|
94
|
-
LaunchDarkly::Result.success(basis)
|
|
99
|
+
rescue => e
|
|
100
|
+
LaunchDarkly::Result.fail("Error fetching test data: #{e.message}", e)
|
|
95
101
|
end
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
end
|
|
102
|
+
|
|
103
|
+
LaunchDarkly::Interfaces::DataSystem::FetchResult.new(result: result, fallback_to_fdv1: false)
|
|
99
104
|
end
|
|
100
105
|
|
|
101
106
|
#
|
|
@@ -109,14 +114,14 @@ module LaunchDarkly
|
|
|
109
114
|
#
|
|
110
115
|
def sync(selector_store)
|
|
111
116
|
# First yield initial data
|
|
112
|
-
|
|
113
|
-
unless
|
|
117
|
+
initial_fetch = fetch(selector_store)
|
|
118
|
+
unless initial_fetch.success?
|
|
114
119
|
yield LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
115
120
|
state: LaunchDarkly::Interfaces::DataSource::Status::OFF,
|
|
116
121
|
error: LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
117
122
|
LaunchDarkly::Interfaces::DataSource::ErrorInfo::STORE_ERROR,
|
|
118
123
|
0,
|
|
119
|
-
|
|
124
|
+
initial_fetch.error,
|
|
120
125
|
Time.now
|
|
121
126
|
)
|
|
122
127
|
)
|
|
@@ -126,7 +131,7 @@ module LaunchDarkly
|
|
|
126
131
|
# Yield the initial successful state
|
|
127
132
|
yield LaunchDarkly::Interfaces::DataSystem::Update.new(
|
|
128
133
|
state: LaunchDarkly::Interfaces::DataSource::Status::VALID,
|
|
129
|
-
change_set:
|
|
134
|
+
change_set: initial_fetch.value.change_set
|
|
130
135
|
)
|
|
131
136
|
|
|
132
137
|
# Continue yielding updates as they arrive
|
|
@@ -32,8 +32,12 @@ module LaunchDarkly
|
|
|
32
32
|
# @option opts [String] :url shortcut for setting the `url` property of the Consul client configuration
|
|
33
33
|
# @option opts [String] :prefix namespace prefix to add to all keys used by LaunchDarkly
|
|
34
34
|
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
|
35
|
-
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
|
36
|
-
#
|
|
35
|
+
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching.
|
|
36
|
+
# When the SDK is configured to use FDv2, the persistent-store cache is automatically
|
|
37
|
+
# dropped once the in-memory store has been initialized, so this setting only affects
|
|
38
|
+
# the brief bootstrap window before the first set of flag data has been received.
|
|
39
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache.
|
|
40
|
+
# Same FDv2 caveat as `:expiration` applies.
|
|
37
41
|
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
|
38
42
|
#
|
|
39
43
|
def self.new_feature_store(opts = {})
|
|
@@ -42,8 +42,12 @@ module LaunchDarkly
|
|
|
42
42
|
# @option opts [Object] :existing_client an already-constructed DynamoDB client for the feature store to use
|
|
43
43
|
# @option opts [String] :prefix namespace prefix to add to all keys used by LaunchDarkly
|
|
44
44
|
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
|
45
|
-
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
|
46
|
-
#
|
|
45
|
+
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching.
|
|
46
|
+
# When the SDK is configured to use FDv2, the persistent-store cache is automatically
|
|
47
|
+
# dropped once the in-memory store has been initialized, so this setting only affects
|
|
48
|
+
# the brief bootstrap window before the first set of flag data has been received.
|
|
49
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache.
|
|
50
|
+
# Same FDv2 caveat as `:expiration` applies.
|
|
47
51
|
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
|
48
52
|
#
|
|
49
53
|
def self.new_feature_store(table_name, opts = {})
|
|
@@ -108,9 +108,6 @@ module LaunchDarkly
|
|
|
108
108
|
#
|
|
109
109
|
# Returns a builder for the FDv2-compatible file data source.
|
|
110
110
|
#
|
|
111
|
-
# This method is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
|
|
112
|
-
# It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
|
|
113
|
-
#
|
|
114
111
|
# This method returns a builder proc that can be used with the FDv2 data system
|
|
115
112
|
# configuration as both an Initializer and a Synchronizer. When used as an Initializer
|
|
116
113
|
# (via `fetch`), it reads files once. When used as a Synchronizer (via `sync`), it
|
|
@@ -50,8 +50,12 @@ module LaunchDarkly
|
|
|
50
50
|
# @option opts [String] :prefix (default_prefix) namespace prefix to add to all hash keys used by LaunchDarkly
|
|
51
51
|
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
|
52
52
|
# @option opts [Integer] :max_connections size of the Redis connection pool
|
|
53
|
-
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
|
54
|
-
#
|
|
53
|
+
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching.
|
|
54
|
+
# When the SDK is configured to use FDv2, the persistent-store cache is automatically
|
|
55
|
+
# dropped once the in-memory store has been initialized, so this setting only affects
|
|
56
|
+
# the brief bootstrap window before the first set of flag data has been received.
|
|
57
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache.
|
|
58
|
+
# Same FDv2 caveat as `:expiration` applies.
|
|
55
59
|
# @option opts [Object] :pool custom connection pool, if desired
|
|
56
60
|
# @option opts [Boolean] :pool_shutdown_on_close whether calling `close` should shutdown the custom connection pool;
|
|
57
61
|
# this is true by default, and should be set to false only if you are managing the pool yourself and want its
|
|
@@ -9,9 +9,6 @@ module LaunchDarkly
|
|
|
9
9
|
# A mechanism for providing dynamically updatable feature flag state in a
|
|
10
10
|
# simplified form to an SDK client in test scenarios using the FDv2 protocol.
|
|
11
11
|
#
|
|
12
|
-
# This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
|
|
13
|
-
# It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
|
|
14
|
-
#
|
|
15
12
|
# Unlike {LaunchDarkly::Integrations::FileData}, this mechanism does not use any external resources. It
|
|
16
13
|
# provides only the data that the application has put into it using the {#update} method.
|
|
17
14
|
#
|
|
@@ -61,50 +61,52 @@ module LaunchDarkly
|
|
|
61
61
|
@core.init_internal(all_data)
|
|
62
62
|
@inited.make_true
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
cache = @cache
|
|
65
|
+
unless cache.nil?
|
|
66
|
+
cache.clear
|
|
66
67
|
all_data.each do |kind, items|
|
|
67
|
-
|
|
68
|
+
cache[kind] = items_if_not_deleted(items)
|
|
68
69
|
items.each do |key, item|
|
|
69
|
-
|
|
70
|
+
cache[item_cache_key(kind, key)] = [item]
|
|
70
71
|
end
|
|
71
72
|
end
|
|
72
73
|
end
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
def get(kind, key)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
cache = @cache
|
|
78
|
+
cache_key = item_cache_key(kind, key)
|
|
79
|
+
unless cache.nil?
|
|
80
|
+
cached = cache[cache_key] # note, item entries in the cache are wrapped in an array so we can cache nil values
|
|
79
81
|
return item_if_not_deleted(cached[0]) unless cached.nil?
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
item = @core.get_internal(kind, key)
|
|
83
85
|
|
|
84
|
-
unless
|
|
85
|
-
@cache[cache_key] = [item]
|
|
86
|
-
end
|
|
86
|
+
cache[cache_key] = [item] unless cache.nil?
|
|
87
87
|
|
|
88
88
|
item_if_not_deleted(item)
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def all(kind)
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
cache = @cache
|
|
93
|
+
unless cache.nil?
|
|
94
|
+
items = cache[all_cache_key(kind)]
|
|
94
95
|
return items unless items.nil?
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
items = items_if_not_deleted(@core.get_all_internal(kind))
|
|
98
|
-
|
|
99
|
+
cache[all_cache_key(kind)] = items unless cache.nil?
|
|
99
100
|
items
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
def upsert(kind, item)
|
|
103
104
|
new_state = @core.upsert_internal(kind, item)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
cache = @cache
|
|
107
|
+
unless cache.nil?
|
|
108
|
+
cache[item_cache_key(kind, item[:key])] = [new_state]
|
|
109
|
+
cache.delete(all_cache_key(kind))
|
|
108
110
|
end
|
|
109
111
|
end
|
|
110
112
|
|
|
@@ -115,13 +117,14 @@ module LaunchDarkly
|
|
|
115
117
|
def initialized?
|
|
116
118
|
return true if @inited.value
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
cache = @cache
|
|
121
|
+
if cache.nil?
|
|
119
122
|
result = @core.initialized_internal?
|
|
120
123
|
else
|
|
121
|
-
result =
|
|
124
|
+
result = cache[inited_cache_key]
|
|
122
125
|
if result.nil?
|
|
123
126
|
result = @core.initialized_internal?
|
|
124
|
-
|
|
127
|
+
cache[inited_cache_key] = result
|
|
125
128
|
end
|
|
126
129
|
end
|
|
127
130
|
|
|
@@ -129,6 +132,23 @@ module LaunchDarkly
|
|
|
129
132
|
result
|
|
130
133
|
end
|
|
131
134
|
|
|
135
|
+
#
|
|
136
|
+
# Disable the in-memory cache. Releases the cache reference so subsequent operations
|
|
137
|
+
# bypass it and go directly to the underlying core. Safe to call multiple times.
|
|
138
|
+
#
|
|
139
|
+
# Called by the FDv2 store coordinator once the in-memory store has become the
|
|
140
|
+
# source of truth and the persistent-store cache is no longer useful. Internal --
|
|
141
|
+
# not part of the public API.
|
|
142
|
+
#
|
|
143
|
+
# @return [void]
|
|
144
|
+
#
|
|
145
|
+
def disable_cache
|
|
146
|
+
cache = @cache
|
|
147
|
+
return if cache.nil?
|
|
148
|
+
@cache = nil
|
|
149
|
+
cache.clear
|
|
150
|
+
end
|
|
151
|
+
|
|
132
152
|
def stop
|
|
133
153
|
@core.stop
|
|
134
154
|
end
|