launchdarkly-server-sdk 8.11.1 → 8.11.3
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 +66 -3
- data/lib/ldclient-rb/context.rb +1 -1
- data/lib/ldclient-rb/data_system.rb +243 -0
- data/lib/ldclient-rb/events.rb +35 -20
- data/lib/ldclient-rb/flags_state.rb +1 -1
- data/lib/ldclient-rb/impl/big_segments.rb +4 -4
- data/lib/ldclient-rb/impl/cache_store.rb +44 -0
- data/lib/ldclient-rb/impl/data_source/null_processor.rb +52 -0
- data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
- data/lib/ldclient-rb/impl/data_source/requestor.rb +106 -0
- data/lib/ldclient-rb/impl/data_source/status_provider.rb +78 -0
- data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
- data/lib/ldclient-rb/impl/data_source.rb +3 -3
- data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
- data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
- data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
- data/lib/ldclient-rb/impl/data_store/status_provider.rb +82 -0
- data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
- data/lib/ldclient-rb/impl/data_store.rb +11 -97
- data/lib/ldclient-rb/impl/data_system/fdv1.rb +178 -0
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +471 -0
- data/lib/ldclient-rb/impl/data_system/polling.rb +601 -0
- data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
- data/lib/ldclient-rb/impl/data_system.rb +298 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
- data/lib/ldclient-rb/impl/evaluator.rb +3 -2
- data/lib/ldclient-rb/impl/event_sender.rb +4 -3
- data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +9 -9
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +0 -1
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +288 -0
- data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
- data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
- data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
- data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
- data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
- data/lib/ldclient-rb/impl/util.rb +65 -0
- data/lib/ldclient-rb/impl.rb +1 -2
- data/lib/ldclient-rb/in_memory_store.rb +1 -18
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
- data/lib/ldclient-rb/integrations/test_data.rb +11 -11
- data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
- data/lib/ldclient-rb/integrations/test_data_v2.rb +248 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
- data/lib/ldclient-rb/interfaces/data_system.rb +755 -0
- data/lib/ldclient-rb/interfaces/feature_store.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +55 -149
- data/lib/ldclient-rb/util.rb +11 -70
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +8 -17
- metadata +52 -17
- data/lib/ldclient-rb/cache_store.rb +0 -45
- data/lib/ldclient-rb/expiring_cache.rb +0 -77
- data/lib/ldclient-rb/memoized_value.rb +0 -32
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
- data/lib/ldclient-rb/polling.rb +0 -102
- data/lib/ldclient-rb/requestor.rb +0 -102
- data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
- data/lib/ldclient-rb/stream.rb +0 -196
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: launchdarkly-server-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.11.
|
|
4
|
+
version: 8.11.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-dynamodb
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.57'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: bigdecimal
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 3.1.1
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 3.1.1
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: bundler
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -284,22 +298,22 @@ dependencies:
|
|
|
284
298
|
name: openssl
|
|
285
299
|
requirement: !ruby/object:Gem::Requirement
|
|
286
300
|
requirements:
|
|
287
|
-
- - "~>"
|
|
288
|
-
- !ruby/object:Gem::Version
|
|
289
|
-
version: '3.1'
|
|
290
301
|
- - ">="
|
|
291
302
|
- !ruby/object:Gem::Version
|
|
292
303
|
version: 3.1.2
|
|
304
|
+
- - "<"
|
|
305
|
+
- !ruby/object:Gem::Version
|
|
306
|
+
version: '5.0'
|
|
293
307
|
type: :runtime
|
|
294
308
|
prerelease: false
|
|
295
309
|
version_requirements: !ruby/object:Gem::Requirement
|
|
296
310
|
requirements:
|
|
297
|
-
- - "~>"
|
|
298
|
-
- !ruby/object:Gem::Version
|
|
299
|
-
version: '3.1'
|
|
300
311
|
- - ">="
|
|
301
312
|
- !ruby/object:Gem::Version
|
|
302
313
|
version: 3.1.2
|
|
314
|
+
- - "<"
|
|
315
|
+
- !ruby/object:Gem::Version
|
|
316
|
+
version: '5.0'
|
|
303
317
|
- !ruby/object:Gem::Dependency
|
|
304
318
|
name: semantic
|
|
305
319
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -373,20 +387,35 @@ files:
|
|
|
373
387
|
- README.md
|
|
374
388
|
- lib/launchdarkly-server-sdk.rb
|
|
375
389
|
- lib/ldclient-rb.rb
|
|
376
|
-
- lib/ldclient-rb/cache_store.rb
|
|
377
390
|
- lib/ldclient-rb/config.rb
|
|
378
391
|
- lib/ldclient-rb/context.rb
|
|
392
|
+
- lib/ldclient-rb/data_system.rb
|
|
379
393
|
- lib/ldclient-rb/evaluation_detail.rb
|
|
380
394
|
- lib/ldclient-rb/events.rb
|
|
381
|
-
- lib/ldclient-rb/expiring_cache.rb
|
|
382
395
|
- lib/ldclient-rb/flags_state.rb
|
|
383
396
|
- lib/ldclient-rb/impl.rb
|
|
384
397
|
- lib/ldclient-rb/impl/big_segments.rb
|
|
385
398
|
- lib/ldclient-rb/impl/broadcaster.rb
|
|
399
|
+
- lib/ldclient-rb/impl/cache_store.rb
|
|
386
400
|
- lib/ldclient-rb/impl/context.rb
|
|
387
401
|
- lib/ldclient-rb/impl/context_filter.rb
|
|
388
402
|
- lib/ldclient-rb/impl/data_source.rb
|
|
403
|
+
- lib/ldclient-rb/impl/data_source/null_processor.rb
|
|
404
|
+
- lib/ldclient-rb/impl/data_source/polling.rb
|
|
405
|
+
- lib/ldclient-rb/impl/data_source/requestor.rb
|
|
406
|
+
- lib/ldclient-rb/impl/data_source/status_provider.rb
|
|
407
|
+
- lib/ldclient-rb/impl/data_source/stream.rb
|
|
389
408
|
- lib/ldclient-rb/impl/data_store.rb
|
|
409
|
+
- lib/ldclient-rb/impl/data_store/data_kind.rb
|
|
410
|
+
- lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb
|
|
411
|
+
- lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb
|
|
412
|
+
- lib/ldclient-rb/impl/data_store/status_provider.rb
|
|
413
|
+
- lib/ldclient-rb/impl/data_store/store.rb
|
|
414
|
+
- lib/ldclient-rb/impl/data_system.rb
|
|
415
|
+
- lib/ldclient-rb/impl/data_system/fdv1.rb
|
|
416
|
+
- lib/ldclient-rb/impl/data_system/fdv2.rb
|
|
417
|
+
- lib/ldclient-rb/impl/data_system/polling.rb
|
|
418
|
+
- lib/ldclient-rb/impl/data_system/protocolv2.rb
|
|
390
419
|
- lib/ldclient-rb/impl/dependency_tracker.rb
|
|
391
420
|
- lib/ldclient-rb/impl/diagnostic_events.rb
|
|
392
421
|
- lib/ldclient-rb/impl/evaluation_with_hook_result.rb
|
|
@@ -397,12 +426,15 @@ files:
|
|
|
397
426
|
- lib/ldclient-rb/impl/event_sender.rb
|
|
398
427
|
- lib/ldclient-rb/impl/event_summarizer.rb
|
|
399
428
|
- lib/ldclient-rb/impl/event_types.rb
|
|
429
|
+
- lib/ldclient-rb/impl/expiring_cache.rb
|
|
400
430
|
- lib/ldclient-rb/impl/flag_tracker.rb
|
|
401
431
|
- lib/ldclient-rb/impl/integrations/consul_impl.rb
|
|
402
432
|
- lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
|
|
403
433
|
- lib/ldclient-rb/impl/integrations/file_data_source.rb
|
|
404
434
|
- lib/ldclient-rb/impl/integrations/redis_impl.rb
|
|
405
435
|
- lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb
|
|
436
|
+
- lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb
|
|
437
|
+
- lib/ldclient-rb/impl/memoized_value.rb
|
|
406
438
|
- lib/ldclient-rb/impl/migrations/migrator.rb
|
|
407
439
|
- lib/ldclient-rb/impl/migrations/tracker.rb
|
|
408
440
|
- lib/ldclient-rb/impl/model/clause.rb
|
|
@@ -410,8 +442,10 @@ files:
|
|
|
410
442
|
- lib/ldclient-rb/impl/model/preprocessed_data.rb
|
|
411
443
|
- lib/ldclient-rb/impl/model/segment.rb
|
|
412
444
|
- lib/ldclient-rb/impl/model/serialization.rb
|
|
445
|
+
- lib/ldclient-rb/impl/non_blocking_thread_pool.rb
|
|
413
446
|
- lib/ldclient-rb/impl/repeating_task.rb
|
|
414
447
|
- lib/ldclient-rb/impl/sampler.rb
|
|
448
|
+
- lib/ldclient-rb/impl/simple_lru_cache.rb
|
|
415
449
|
- lib/ldclient-rb/impl/store_client_wrapper.rb
|
|
416
450
|
- lib/ldclient-rb/impl/store_data_set_sorter.rb
|
|
417
451
|
- lib/ldclient-rb/impl/unbounded_pool.rb
|
|
@@ -424,31 +458,32 @@ files:
|
|
|
424
458
|
- lib/ldclient-rb/integrations/redis.rb
|
|
425
459
|
- lib/ldclient-rb/integrations/test_data.rb
|
|
426
460
|
- lib/ldclient-rb/integrations/test_data/flag_builder.rb
|
|
461
|
+
- lib/ldclient-rb/integrations/test_data_v2.rb
|
|
462
|
+
- lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb
|
|
427
463
|
- lib/ldclient-rb/integrations/util/store_wrapper.rb
|
|
428
464
|
- lib/ldclient-rb/interfaces.rb
|
|
429
465
|
- lib/ldclient-rb/interfaces/big_segment_store.rb
|
|
430
466
|
- lib/ldclient-rb/interfaces/data_source.rb
|
|
431
467
|
- lib/ldclient-rb/interfaces/data_store.rb
|
|
468
|
+
- lib/ldclient-rb/interfaces/data_system.rb
|
|
432
469
|
- lib/ldclient-rb/interfaces/feature_store.rb
|
|
433
470
|
- lib/ldclient-rb/interfaces/flag_tracker.rb
|
|
434
471
|
- lib/ldclient-rb/interfaces/hooks.rb
|
|
435
472
|
- lib/ldclient-rb/interfaces/migrations.rb
|
|
436
473
|
- lib/ldclient-rb/interfaces/plugins.rb
|
|
437
474
|
- lib/ldclient-rb/ldclient.rb
|
|
438
|
-
- lib/ldclient-rb/memoized_value.rb
|
|
439
475
|
- lib/ldclient-rb/migrations.rb
|
|
440
|
-
- lib/ldclient-rb/non_blocking_thread_pool.rb
|
|
441
|
-
- lib/ldclient-rb/polling.rb
|
|
442
476
|
- lib/ldclient-rb/reference.rb
|
|
443
|
-
- lib/ldclient-rb/requestor.rb
|
|
444
|
-
- lib/ldclient-rb/simple_lru_cache.rb
|
|
445
|
-
- lib/ldclient-rb/stream.rb
|
|
446
477
|
- lib/ldclient-rb/util.rb
|
|
447
478
|
- lib/ldclient-rb/version.rb
|
|
448
479
|
homepage: https://github.com/launchdarkly/ruby-server-sdk
|
|
449
480
|
licenses:
|
|
450
481
|
- Apache-2.0
|
|
451
|
-
metadata:
|
|
482
|
+
metadata:
|
|
483
|
+
bug_tracker_uri: https://github.com/launchdarkly/ruby-server-sdk/issues
|
|
484
|
+
changelog_uri: https://github.com/launchdarkly/ruby-server-sdk/blob/main/CHANGELOG.md
|
|
485
|
+
homepage_uri: https://github.com/launchdarkly/ruby-server-sdk
|
|
486
|
+
source_code_uri: https://github.com/launchdarkly/ruby-server-sdk
|
|
452
487
|
post_install_message:
|
|
453
488
|
rdoc_options: []
|
|
454
489
|
require_paths:
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
require "concurrent/map"
|
|
2
|
-
|
|
3
|
-
module LaunchDarkly
|
|
4
|
-
#
|
|
5
|
-
# A thread-safe in-memory store that uses the same semantics that Faraday would expect, although we
|
|
6
|
-
# no longer use Faraday. This is used by Requestor, when we are not in a Rails environment.
|
|
7
|
-
#
|
|
8
|
-
# @private
|
|
9
|
-
#
|
|
10
|
-
class ThreadSafeMemoryStore
|
|
11
|
-
#
|
|
12
|
-
# Default constructor
|
|
13
|
-
#
|
|
14
|
-
# @return [ThreadSafeMemoryStore] a new store
|
|
15
|
-
def initialize
|
|
16
|
-
@cache = Concurrent::Map.new
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
# Read a value from the cache
|
|
21
|
-
# @param key [Object] the cache key
|
|
22
|
-
#
|
|
23
|
-
# @return [Object] the cache value
|
|
24
|
-
def read(key)
|
|
25
|
-
@cache[key]
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
# Store a value in the cache
|
|
30
|
-
# @param key [Object] the cache key
|
|
31
|
-
# @param value [Object] the value to associate with the key
|
|
32
|
-
#
|
|
33
|
-
# @return [Object] the value
|
|
34
|
-
def write(key, value)
|
|
35
|
-
@cache[key] = value
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
# Delete a value in the cache
|
|
40
|
-
# @param key [Object] the cache key
|
|
41
|
-
def delete(key)
|
|
42
|
-
@cache.delete(key)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module LaunchDarkly
|
|
3
|
-
# A thread-safe cache with maximum number of entries and TTL.
|
|
4
|
-
# Adapted from https://github.com/SamSaffron/lru_redux/blob/master/lib/lru_redux/ttl/cache.rb
|
|
5
|
-
# under MIT license with the following changes:
|
|
6
|
-
# * made thread-safe
|
|
7
|
-
# * removed many unused methods
|
|
8
|
-
# * reading a key does not reset its expiration time, only writing
|
|
9
|
-
# @private
|
|
10
|
-
class ExpiringCache
|
|
11
|
-
def initialize(max_size, ttl)
|
|
12
|
-
@max_size = max_size
|
|
13
|
-
@ttl = ttl
|
|
14
|
-
@data_lru = {}
|
|
15
|
-
@data_ttl = {}
|
|
16
|
-
@lock = Mutex.new
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def [](key)
|
|
20
|
-
@lock.synchronize do
|
|
21
|
-
ttl_evict
|
|
22
|
-
@data_lru[key]
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def []=(key, val)
|
|
27
|
-
@lock.synchronize do
|
|
28
|
-
ttl_evict
|
|
29
|
-
|
|
30
|
-
@data_lru.delete(key)
|
|
31
|
-
@data_ttl.delete(key)
|
|
32
|
-
|
|
33
|
-
@data_lru[key] = val
|
|
34
|
-
@data_ttl[key] = Time.now.to_f
|
|
35
|
-
|
|
36
|
-
if @data_lru.size > @max_size
|
|
37
|
-
key, _ = @data_lru.first # hashes have a FIFO ordering in Ruby
|
|
38
|
-
|
|
39
|
-
@data_ttl.delete(key)
|
|
40
|
-
@data_lru.delete(key)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
val
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def delete(key)
|
|
48
|
-
@lock.synchronize do
|
|
49
|
-
ttl_evict
|
|
50
|
-
|
|
51
|
-
@data_lru.delete(key)
|
|
52
|
-
@data_ttl.delete(key)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def clear
|
|
57
|
-
@lock.synchronize do
|
|
58
|
-
@data_lru.clear
|
|
59
|
-
@data_ttl.clear
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def ttl_evict
|
|
66
|
-
ttl_horizon = Time.now.to_f - @ttl
|
|
67
|
-
key, time = @data_ttl.first
|
|
68
|
-
|
|
69
|
-
until time.nil? || time > ttl_horizon
|
|
70
|
-
@data_ttl.delete(key)
|
|
71
|
-
@data_lru.delete(key)
|
|
72
|
-
|
|
73
|
-
key, time = @data_ttl.first
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module LaunchDarkly
|
|
3
|
-
# Simple implementation of a thread-safe memoized value whose generator function will never be
|
|
4
|
-
# run more than once, and whose value can be overridden by explicit assignment.
|
|
5
|
-
# Note that we no longer use this class and it will be removed in a future version.
|
|
6
|
-
# @private
|
|
7
|
-
class MemoizedValue
|
|
8
|
-
def initialize(&generator)
|
|
9
|
-
@generator = generator
|
|
10
|
-
@mutex = Mutex.new
|
|
11
|
-
@inited = false
|
|
12
|
-
@value = nil
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def get
|
|
16
|
-
@mutex.synchronize do
|
|
17
|
-
unless @inited
|
|
18
|
-
@value = @generator.call
|
|
19
|
-
@inited = true
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
@value
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def set(value)
|
|
26
|
-
@mutex.synchronize do
|
|
27
|
-
@value = value
|
|
28
|
-
@inited = true
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
require "concurrent"
|
|
2
|
-
require "concurrent/atomics"
|
|
3
|
-
require "concurrent/executors"
|
|
4
|
-
require "thread"
|
|
5
|
-
|
|
6
|
-
module LaunchDarkly
|
|
7
|
-
# Simple wrapper for a FixedThreadPool that rejects new jobs if all the threads are busy, rather
|
|
8
|
-
# than blocking. Also provides a way to wait for all jobs to finish without shutting down.
|
|
9
|
-
# @private
|
|
10
|
-
class NonBlockingThreadPool
|
|
11
|
-
def initialize(capacity, name = 'LD/NonBlockingThreadPool')
|
|
12
|
-
@capacity = capacity
|
|
13
|
-
@pool = Concurrent::FixedThreadPool.new(capacity, name: name)
|
|
14
|
-
@semaphore = Concurrent::Semaphore.new(capacity)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Attempts to submit a job, but only if a worker is available. Unlike the regular post method,
|
|
18
|
-
# this returns a value: true if the job was submitted, false if all workers are busy.
|
|
19
|
-
def post
|
|
20
|
-
unless @semaphore.try_acquire(1)
|
|
21
|
-
return
|
|
22
|
-
end
|
|
23
|
-
@pool.post do
|
|
24
|
-
begin
|
|
25
|
-
yield
|
|
26
|
-
ensure
|
|
27
|
-
@semaphore.release(1)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Waits until no jobs are executing, without shutting down the pool.
|
|
33
|
-
def wait_all
|
|
34
|
-
@semaphore.acquire(@capacity)
|
|
35
|
-
@semaphore.release(@capacity)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def shutdown
|
|
39
|
-
@pool.shutdown
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def wait_for_termination
|
|
43
|
-
@pool.wait_for_termination
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
data/lib/ldclient-rb/polling.rb
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
require "ldclient-rb/impl/repeating_task"
|
|
2
|
-
|
|
3
|
-
require "concurrent/atomics"
|
|
4
|
-
require "json"
|
|
5
|
-
require "thread"
|
|
6
|
-
|
|
7
|
-
module LaunchDarkly
|
|
8
|
-
# @private
|
|
9
|
-
class PollingProcessor
|
|
10
|
-
def initialize(config, requestor)
|
|
11
|
-
@config = config
|
|
12
|
-
@requestor = requestor
|
|
13
|
-
@initialized = Concurrent::AtomicBoolean.new(false)
|
|
14
|
-
@started = Concurrent::AtomicBoolean.new(false)
|
|
15
|
-
@ready = Concurrent::Event.new
|
|
16
|
-
@task = Impl::RepeatingTask.new(@config.poll_interval, 0, -> { self.poll }, @config.logger, 'LD/PollingDataSource')
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def initialized?
|
|
20
|
-
@initialized.value
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def start
|
|
24
|
-
return @ready unless @started.make_true
|
|
25
|
-
@config.logger.info { "[LDClient] Initializing polling connection" }
|
|
26
|
-
@task.start
|
|
27
|
-
@ready
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def stop
|
|
31
|
-
stop_with_error_info
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def poll
|
|
35
|
-
begin
|
|
36
|
-
all_data = @requestor.request_all_data
|
|
37
|
-
if all_data
|
|
38
|
-
update_sink_or_data_store.init(all_data)
|
|
39
|
-
if @initialized.make_true
|
|
40
|
-
@config.logger.info { "[LDClient] Polling connection initialized" }
|
|
41
|
-
@ready.set
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
@config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil)
|
|
45
|
-
rescue JSON::ParserError => e
|
|
46
|
-
@config.logger.error { "[LDClient] JSON parsing failed for polling response." }
|
|
47
|
-
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
48
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA,
|
|
49
|
-
0,
|
|
50
|
-
e.to_s,
|
|
51
|
-
Time.now
|
|
52
|
-
)
|
|
53
|
-
@config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
|
|
54
|
-
rescue UnexpectedResponseError => e
|
|
55
|
-
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
56
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, e.status, nil, Time.now)
|
|
57
|
-
message = Util.http_error_message(e.status, "polling request", "will retry")
|
|
58
|
-
@config.logger.error { "[LDClient] #{message}" }
|
|
59
|
-
|
|
60
|
-
if Util.http_error_recoverable?(e.status)
|
|
61
|
-
@config.data_source_update_sink&.update_status(
|
|
62
|
-
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
|
63
|
-
error_info
|
|
64
|
-
)
|
|
65
|
-
else
|
|
66
|
-
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
|
67
|
-
stop_with_error_info error_info
|
|
68
|
-
end
|
|
69
|
-
rescue StandardError => e
|
|
70
|
-
Util.log_exception(@config.logger, "Exception while polling", e)
|
|
71
|
-
@config.data_source_update_sink&.update_status(
|
|
72
|
-
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
|
73
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, e.to_s, Time.now)
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
#
|
|
79
|
-
# The original implementation of this class relied on the feature store
|
|
80
|
-
# directly, which we are trying to move away from. Customers who might have
|
|
81
|
-
# instantiated this directly for some reason wouldn't know they have to set
|
|
82
|
-
# the config's sink manually, so we have to fall back to the store if the
|
|
83
|
-
# sink isn't present.
|
|
84
|
-
#
|
|
85
|
-
# The next major release should be able to simplify this structure and
|
|
86
|
-
# remove the need for fall back to the data store because the update sink
|
|
87
|
-
# should always be present.
|
|
88
|
-
#
|
|
89
|
-
private def update_sink_or_data_store
|
|
90
|
-
@config.data_source_update_sink || @config.feature_store
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
#
|
|
94
|
-
# @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info
|
|
95
|
-
#
|
|
96
|
-
private def stop_with_error_info(error_info = nil)
|
|
97
|
-
@task.stop
|
|
98
|
-
@config.logger.info { "[LDClient] Polling connection stopped" }
|
|
99
|
-
@config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
require "ldclient-rb/impl/model/serialization"
|
|
2
|
-
|
|
3
|
-
require "concurrent/atomics"
|
|
4
|
-
require "json"
|
|
5
|
-
require "uri"
|
|
6
|
-
require "http"
|
|
7
|
-
|
|
8
|
-
module LaunchDarkly
|
|
9
|
-
# @private
|
|
10
|
-
class UnexpectedResponseError < StandardError
|
|
11
|
-
def initialize(status)
|
|
12
|
-
@status = status
|
|
13
|
-
super("HTTP error #{status}")
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def status
|
|
17
|
-
@status
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @private
|
|
22
|
-
class Requestor
|
|
23
|
-
CacheEntry = Struct.new(:etag, :body)
|
|
24
|
-
|
|
25
|
-
def initialize(sdk_key, config)
|
|
26
|
-
@sdk_key = sdk_key
|
|
27
|
-
@config = config
|
|
28
|
-
@http_client = LaunchDarkly::Util.new_http_client(config.base_uri, config)
|
|
29
|
-
.use(:auto_inflate)
|
|
30
|
-
.headers("Accept-Encoding" => "gzip")
|
|
31
|
-
@cache = @config.cache_store
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def request_all_data()
|
|
35
|
-
all_data = JSON.parse(make_request("/sdk/latest-all"), symbolize_names: true)
|
|
36
|
-
Impl::Model.make_all_store_data(all_data, @config.logger)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def stop
|
|
40
|
-
begin
|
|
41
|
-
@http_client.close
|
|
42
|
-
rescue
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def make_request(path)
|
|
49
|
-
uri = URI(
|
|
50
|
-
Util.add_payload_filter_key(@config.base_uri + path, @config)
|
|
51
|
-
)
|
|
52
|
-
headers = {}
|
|
53
|
-
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
|
|
54
|
-
headers["Connection"] = "keep-alive"
|
|
55
|
-
cached = @cache.read(uri)
|
|
56
|
-
unless cached.nil?
|
|
57
|
-
headers["If-None-Match"] = cached.etag
|
|
58
|
-
end
|
|
59
|
-
response = @http_client.request("GET", uri, {
|
|
60
|
-
headers: headers,
|
|
61
|
-
})
|
|
62
|
-
status = response.status.code
|
|
63
|
-
# must fully read body for persistent connections
|
|
64
|
-
body = response.to_s
|
|
65
|
-
@config.logger.debug { "[LDClient] Got response from uri: #{uri}\n\tstatus code: #{status}\n\theaders: #{response.headers.to_h}\n\tbody: #{body}" }
|
|
66
|
-
if status == 304 && !cached.nil?
|
|
67
|
-
body = cached.body
|
|
68
|
-
else
|
|
69
|
-
@cache.delete(uri)
|
|
70
|
-
if status < 200 || status >= 300
|
|
71
|
-
raise UnexpectedResponseError.new(status)
|
|
72
|
-
end
|
|
73
|
-
body = fix_encoding(body, response.headers["content-type"])
|
|
74
|
-
etag = response.headers["etag"]
|
|
75
|
-
@cache.write(uri, CacheEntry.new(etag, body)) unless etag.nil?
|
|
76
|
-
end
|
|
77
|
-
body
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def fix_encoding(body, content_type)
|
|
81
|
-
return body if content_type.nil?
|
|
82
|
-
media_type, charset = parse_content_type(content_type)
|
|
83
|
-
return body if charset.nil?
|
|
84
|
-
body.force_encoding(Encoding::find(charset)).encode(Encoding::UTF_8)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def parse_content_type(value)
|
|
88
|
-
return [nil, nil] if value.nil? || value == ''
|
|
89
|
-
parts = value.split(/; */)
|
|
90
|
-
return [value, nil] if parts.count < 2
|
|
91
|
-
charset = nil
|
|
92
|
-
parts.each do |part|
|
|
93
|
-
fields = part.split('=')
|
|
94
|
-
if fields.count >= 2 && fields[0] == 'charset'
|
|
95
|
-
charset = fields[1]
|
|
96
|
-
break
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
[parts[0], charset]
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module LaunchDarkly
|
|
3
|
-
# A non-thread-safe implementation of a LRU cache set with only add and reset methods.
|
|
4
|
-
# Based on https://github.com/SamSaffron/lru_redux/blob/master/lib/lru_redux/cache.rb
|
|
5
|
-
# @private
|
|
6
|
-
class SimpleLRUCacheSet
|
|
7
|
-
def initialize(capacity)
|
|
8
|
-
@values = {}
|
|
9
|
-
@capacity = capacity
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Adds a value to the cache or marks it recent if it was already there. Returns true if already there.
|
|
13
|
-
def add(value)
|
|
14
|
-
found = true
|
|
15
|
-
@values.delete(value) { found = false }
|
|
16
|
-
@values[value] = true
|
|
17
|
-
@values.shift if @values.length > @capacity
|
|
18
|
-
found
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def clear
|
|
22
|
-
@values = {}
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|