ldclient-rb 5.4.3 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +33 -6
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +0 -12
- data/Gemfile.lock +22 -3
- data/README.md +41 -35
- data/ldclient-rb.gemspec +4 -3
- data/lib/ldclient-rb.rb +9 -1
- data/lib/ldclient-rb/cache_store.rb +1 -0
- data/lib/ldclient-rb/config.rb +201 -90
- data/lib/ldclient-rb/evaluation.rb +56 -8
- data/lib/ldclient-rb/event_summarizer.rb +3 -0
- data/lib/ldclient-rb/events.rb +16 -0
- data/lib/ldclient-rb/expiring_cache.rb +1 -0
- data/lib/ldclient-rb/file_data_source.rb +18 -13
- data/lib/ldclient-rb/flags_state.rb +3 -2
- data/lib/ldclient-rb/impl.rb +13 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +158 -0
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +228 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +155 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +47 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +55 -0
- data/lib/ldclient-rb/in_memory_store.rb +15 -4
- data/lib/ldclient-rb/integrations.rb +55 -0
- data/lib/ldclient-rb/integrations/consul.rb +38 -0
- data/lib/ldclient-rb/integrations/dynamodb.rb +47 -0
- data/lib/ldclient-rb/integrations/redis.rb +55 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +230 -0
- data/lib/ldclient-rb/interfaces.rb +153 -0
- data/lib/ldclient-rb/ldclient.rb +135 -77
- data/lib/ldclient-rb/memoized_value.rb +2 -0
- data/lib/ldclient-rb/newrelic.rb +1 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +3 -3
- data/lib/ldclient-rb/polling.rb +1 -0
- data/lib/ldclient-rb/redis_store.rb +24 -190
- data/lib/ldclient-rb/requestor.rb +3 -2
- data/lib/ldclient-rb/simple_lru_cache.rb +1 -0
- data/lib/ldclient-rb/stream.rb +22 -10
- data/lib/ldclient-rb/user_filter.rb +1 -0
- data/lib/ldclient-rb/util.rb +1 -0
- data/lib/ldclient-rb/version.rb +1 -1
- data/scripts/gendocs.sh +12 -0
- data/spec/feature_store_spec_base.rb +173 -72
- data/spec/file_data_source_spec.rb +2 -2
- data/spec/http_util.rb +103 -0
- data/spec/in_memory_feature_store_spec.rb +1 -1
- data/spec/integrations/consul_feature_store_spec.rb +41 -0
- data/spec/integrations/dynamodb_feature_store_spec.rb +104 -0
- data/spec/integrations/store_wrapper_spec.rb +276 -0
- data/spec/ldclient_spec.rb +83 -4
- data/spec/redis_feature_store_spec.rb +25 -16
- data/spec/requestor_spec.rb +44 -38
- data/spec/stream_spec.rb +18 -18
- metadata +55 -33
- data/lib/sse_client.rb +0 -4
- data/lib/sse_client/backoff.rb +0 -38
- data/lib/sse_client/sse_client.rb +0 -171
- data/lib/sse_client/sse_events.rb +0 -67
- data/lib/sse_client/streaming_http.rb +0 -199
- data/spec/sse_client/sse_client_spec.rb +0 -177
- data/spec/sse_client/sse_events_spec.rb +0 -100
- data/spec/sse_client/sse_shared.rb +0 -82
- data/spec/sse_client/streaming_http_spec.rb +0 -263
@@ -6,20 +6,31 @@ module LaunchDarkly
|
|
6
6
|
# we add another storable data type in the future, as long as it follows the same pattern
|
7
7
|
# (having "key", "version", and "deleted" properties), we only need to add a corresponding
|
8
8
|
# constant here and the existing store should be able to handle it.
|
9
|
+
#
|
10
|
+
# The :priority and :get_dependency_keys properties are used by FeatureStoreDataSetSorter
|
11
|
+
# to ensure data consistency during non-atomic updates.
|
12
|
+
|
13
|
+
# @private
|
9
14
|
FEATURES = {
|
10
|
-
namespace: "features"
|
15
|
+
namespace: "features",
|
16
|
+
priority: 1, # that is, features should be stored after segments
|
17
|
+
get_dependency_keys: lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }
|
11
18
|
}.freeze
|
12
19
|
|
20
|
+
# @private
|
13
21
|
SEGMENTS = {
|
14
|
-
namespace: "segments"
|
22
|
+
namespace: "segments",
|
23
|
+
priority: 0
|
15
24
|
}.freeze
|
16
25
|
|
17
26
|
#
|
18
27
|
# Default implementation of the LaunchDarkly client's feature store, using an in-memory
|
19
|
-
# cache. This object holds feature flags and related data received from
|
20
|
-
#
|
28
|
+
# cache. This object holds feature flags and related data received from LaunchDarkly.
|
29
|
+
# Database-backed implementations are available in {LaunchDarkly::Integrations}.
|
21
30
|
#
|
22
31
|
class InMemoryFeatureStore
|
32
|
+
include LaunchDarkly::Interfaces::FeatureStore
|
33
|
+
|
23
34
|
def initialize
|
24
35
|
@items = Hash.new
|
25
36
|
@lock = Concurrent::ReadWriteLock.new
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "ldclient-rb/integrations/consul"
|
2
|
+
require "ldclient-rb/integrations/dynamodb"
|
3
|
+
require "ldclient-rb/integrations/redis"
|
4
|
+
require "ldclient-rb/integrations/util/store_wrapper"
|
5
|
+
|
6
|
+
module LaunchDarkly
|
7
|
+
#
|
8
|
+
# Tools for connecting the LaunchDarkly client to other software.
|
9
|
+
#
|
10
|
+
module Integrations
|
11
|
+
#
|
12
|
+
# Integration with [Consul](https://www.consul.io/).
|
13
|
+
#
|
14
|
+
# Note that in order to use this integration, you must first install the gem `diplomat`.
|
15
|
+
#
|
16
|
+
# @since 5.5.0
|
17
|
+
#
|
18
|
+
module Consul
|
19
|
+
# code is in ldclient-rb/impl/integrations/consul_impl
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Integration with [DynamoDB](https://aws.amazon.com/dynamodb/).
|
24
|
+
#
|
25
|
+
# Note that in order to use this integration, you must first install one of the AWS SDK gems: either
|
26
|
+
# `aws-sdk-dynamodb`, or the full `aws-sdk`.
|
27
|
+
#
|
28
|
+
# @since 5.5.0
|
29
|
+
#
|
30
|
+
module DynamoDB
|
31
|
+
# code is in ldclient-rb/impl/integrations/dynamodb_impl
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Integration with [Redis](https://redis.io/).
|
36
|
+
#
|
37
|
+
# Note that in order to use this integration, you must first install the `redis` and `connection-pool`
|
38
|
+
# gems.
|
39
|
+
#
|
40
|
+
# @since 5.5.0
|
41
|
+
#
|
42
|
+
module Redis
|
43
|
+
# code is in ldclient-rb/impl/integrations/redis_impl
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Support code that may be helpful in creating integrations.
|
48
|
+
#
|
49
|
+
# @since 5.5.0
|
50
|
+
#
|
51
|
+
module Util
|
52
|
+
# code is in ldclient-rb/integrations/util/
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "ldclient-rb/impl/integrations/consul_impl"
|
2
|
+
require "ldclient-rb/integrations/util/store_wrapper"
|
3
|
+
|
4
|
+
module LaunchDarkly
|
5
|
+
module Integrations
|
6
|
+
module Consul
|
7
|
+
#
|
8
|
+
# Default value for the `prefix` option for {new_feature_store}.
|
9
|
+
#
|
10
|
+
# @return [String] the default key prefix
|
11
|
+
#
|
12
|
+
def self.default_prefix
|
13
|
+
'launchdarkly'
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Creates a Consul-backed persistent feature store.
|
18
|
+
#
|
19
|
+
# To use this method, you must first install the gem `diplomat`. Then, put the object returned by
|
20
|
+
# this method into the `feature_store` property of your client configuration ({LaunchDarkly::Config}).
|
21
|
+
#
|
22
|
+
# @param opts [Hash] the configuration options
|
23
|
+
# @option opts [Hash] :consul_config an instance of `Diplomat::Configuration` to replace the default
|
24
|
+
# Consul client configuration (note that this is exactly the same as modifying `Diplomat.configuration`)
|
25
|
+
# @option opts [String] :url shortcut for setting the `url` property of the Consul client configuration
|
26
|
+
# @option opts [String] :prefix namespace prefix to add to all keys used by LaunchDarkly
|
27
|
+
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
28
|
+
# @option opts [Integer] :expiration_seconds (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
29
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
30
|
+
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
31
|
+
#
|
32
|
+
def self.new_feature_store(opts, &block)
|
33
|
+
core = LaunchDarkly::Impl::Integrations::Consul::ConsulFeatureStoreCore.new(opts)
|
34
|
+
return LaunchDarkly::Integrations::Util::CachingStoreWrapper.new(core, opts)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "ldclient-rb/impl/integrations/dynamodb_impl"
|
2
|
+
require "ldclient-rb/integrations/util/store_wrapper"
|
3
|
+
|
4
|
+
module LaunchDarkly
|
5
|
+
module Integrations
|
6
|
+
module DynamoDB
|
7
|
+
#
|
8
|
+
# Creates a DynamoDB-backed persistent feature store. For more details about how and why you can
|
9
|
+
# use a persistent feature store, see the
|
10
|
+
# [SDK reference guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store).
|
11
|
+
#
|
12
|
+
# To use this method, you must first install one of the AWS SDK gems: either `aws-sdk-dynamodb`, or
|
13
|
+
# the full `aws-sdk`. Then, put the object returned by this method into the `feature_store` property
|
14
|
+
# of your client configuration ({LaunchDarkly::Config}).
|
15
|
+
#
|
16
|
+
# @example Configuring the feature store
|
17
|
+
# store = LaunchDarkly::Integrations::DynamoDB::new_feature_store("my-table-name")
|
18
|
+
# config = LaunchDarkly::Config.new(feature_store: store)
|
19
|
+
# client = LaunchDarkly::LDClient.new(my_sdk_key, config)
|
20
|
+
#
|
21
|
+
# Note that the specified table must already exist in DynamoDB. It must have a partition key called
|
22
|
+
# "namespace", and a sort key called "key" (both strings). The SDK does not create the table
|
23
|
+
# automatically because it has no way of knowing what additional properties (such as permissions
|
24
|
+
# and throughput) you would want it to have.
|
25
|
+
#
|
26
|
+
# By default, the DynamoDB client will try to get your AWS credentials and region name from
|
27
|
+
# environment variables and/or local configuration files, as described in the AWS SDK documentation.
|
28
|
+
# You can also specify any supported AWS SDK options in `dynamodb_opts`-- or, provide an
|
29
|
+
# already-configured DynamoDB client in `existing_client`.
|
30
|
+
#
|
31
|
+
# @param table_name [String] name of an existing DynamoDB table
|
32
|
+
# @param opts [Hash] the configuration options
|
33
|
+
# @option opts [Hash] :dynamodb_opts options to pass to the DynamoDB client constructor (ignored if you specify `:existing_client`)
|
34
|
+
# @option opts [Object] :existing_client an already-constructed DynamoDB client for the feature store to use
|
35
|
+
# @option opts [String] :prefix namespace prefix to add to all keys used by LaunchDarkly
|
36
|
+
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
37
|
+
# @option opts [Integer] :expiration_seconds (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
38
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
39
|
+
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
40
|
+
#
|
41
|
+
def self.new_feature_store(table_name, opts)
|
42
|
+
core = LaunchDarkly::Impl::Integrations::DynamoDB::DynamoDBFeatureStoreCore.new(table_name, opts)
|
43
|
+
return LaunchDarkly::Integrations::Util::CachingStoreWrapper.new(core, opts)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "ldclient-rb/redis_store" # eventually we will just refer to impl/integrations/redis_impl directly
|
2
|
+
|
3
|
+
module LaunchDarkly
|
4
|
+
module Integrations
|
5
|
+
module Redis
|
6
|
+
#
|
7
|
+
# Default value for the `redis_url` option for {new_feature_store}. This points to an instance of
|
8
|
+
# Redis running at `localhost` with its default port.
|
9
|
+
#
|
10
|
+
# @return [String] the default Redis URL
|
11
|
+
#
|
12
|
+
def self.default_redis_url
|
13
|
+
'redis://localhost:6379/0'
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Default value for the `prefix` option for {new_feature_store}.
|
18
|
+
#
|
19
|
+
# @return [String] the default key prefix
|
20
|
+
#
|
21
|
+
def self.default_prefix
|
22
|
+
'launchdarkly'
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Creates a Redis-backed persistent feature store. For more details about how and why you can
|
27
|
+
# use a persistent feature store, see the
|
28
|
+
# [SDK reference guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store).
|
29
|
+
#
|
30
|
+
# To use this method, you must first have the `redis` and `connection-pool` gems installed. Then,
|
31
|
+
# put the object returned by this method into the `feature_store` property of your
|
32
|
+
# client configuration.
|
33
|
+
#
|
34
|
+
# @example Configuring the feature store
|
35
|
+
# store = LaunchDarkly::Integrations::Redis::new_feature_store(redis_url: "redis://my-server")
|
36
|
+
# config = LaunchDarkly::Config.new(feature_store: store)
|
37
|
+
# client = LaunchDarkly::LDClient.new(my_sdk_key, config)
|
38
|
+
#
|
39
|
+
# @param opts [Hash] the configuration options
|
40
|
+
# @option opts [String] :redis_url (default_redis_url) URL of the Redis instance (shortcut for omitting `redis_opts`)
|
41
|
+
# @option opts [Hash] :redis_opts options to pass to the Redis constructor (if you want to specify more than just `redis_url`)
|
42
|
+
# @option opts [String] :prefix (default_prefix) namespace prefix to add to all hash keys used by LaunchDarkly
|
43
|
+
# @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
|
44
|
+
# @option opts [Integer] :max_connections size of the Redis connection pool
|
45
|
+
# @option opts [Integer] :expiration_seconds (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
46
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
47
|
+
# @option opts [Object] :pool custom connection pool, if desired
|
48
|
+
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
49
|
+
#
|
50
|
+
def self.new_feature_store(opts)
|
51
|
+
return RedisFeatureStore.new(opts)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require "concurrent/atomics"
|
2
|
+
|
3
|
+
require "ldclient-rb/expiring_cache"
|
4
|
+
|
5
|
+
module LaunchDarkly
|
6
|
+
module Integrations
|
7
|
+
module Util
|
8
|
+
#
|
9
|
+
# CachingStoreWrapper is a partial implementation of the {LaunchDarkly::Interfaces::FeatureStore}
|
10
|
+
# pattern that delegates part of its behavior to another object, while providing optional caching
|
11
|
+
# behavior and other logic that would otherwise be repeated in every feature store implementation.
|
12
|
+
# This makes it easier to create new database integrations by implementing only the database-specific
|
13
|
+
# logic.
|
14
|
+
#
|
15
|
+
# The mixin {FeatureStoreCore} describes the methods that need to be supported by the inner
|
16
|
+
# implementation object.
|
17
|
+
#
|
18
|
+
class CachingStoreWrapper
|
19
|
+
include LaunchDarkly::Interfaces::FeatureStore
|
20
|
+
|
21
|
+
#
|
22
|
+
# Creates a new store wrapper instance.
|
23
|
+
#
|
24
|
+
# @param core [Object] an object that implements the {FeatureStoreCore} methods
|
25
|
+
# @param opts [Hash] a hash that may include cache-related options; all others will be ignored
|
26
|
+
# @option opts [Float] :expiration_seconds (15) cache TTL; zero means no caching
|
27
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
28
|
+
#
|
29
|
+
def initialize(core, opts)
|
30
|
+
@core = core
|
31
|
+
|
32
|
+
expiration_seconds = opts[:expiration] || 15
|
33
|
+
if expiration_seconds > 0
|
34
|
+
capacity = opts[:capacity] || 1000
|
35
|
+
@cache = ExpiringCache.new(capacity, expiration_seconds)
|
36
|
+
else
|
37
|
+
@cache = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
@inited = Concurrent::AtomicBoolean.new(false)
|
41
|
+
end
|
42
|
+
|
43
|
+
def init(all_data)
|
44
|
+
@core.init_internal(all_data)
|
45
|
+
@inited.make_true
|
46
|
+
|
47
|
+
if !@cache.nil?
|
48
|
+
@cache.clear
|
49
|
+
all_data.each do |kind, items|
|
50
|
+
@cache[kind] = items_if_not_deleted(items)
|
51
|
+
items.each do |key, item|
|
52
|
+
@cache[item_cache_key(kind, key)] = [item]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get(kind, key)
|
59
|
+
if !@cache.nil?
|
60
|
+
cache_key = item_cache_key(kind, key)
|
61
|
+
cached = @cache[cache_key] # note, item entries in the cache are wrapped in an array so we can cache nil values
|
62
|
+
return item_if_not_deleted(cached[0]) if !cached.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
item = @core.get_internal(kind, key)
|
66
|
+
|
67
|
+
if !@cache.nil?
|
68
|
+
@cache[cache_key] = [item]
|
69
|
+
end
|
70
|
+
|
71
|
+
item_if_not_deleted(item)
|
72
|
+
end
|
73
|
+
|
74
|
+
def all(kind)
|
75
|
+
if !@cache.nil?
|
76
|
+
items = @cache[all_cache_key(kind)]
|
77
|
+
return items if !items.nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
items = items_if_not_deleted(@core.get_all_internal(kind))
|
81
|
+
@cache[all_cache_key(kind)] = items if !@cache.nil?
|
82
|
+
items
|
83
|
+
end
|
84
|
+
|
85
|
+
def upsert(kind, item)
|
86
|
+
new_state = @core.upsert_internal(kind, item)
|
87
|
+
|
88
|
+
if !@cache.nil?
|
89
|
+
@cache[item_cache_key(kind, item[:key])] = [new_state]
|
90
|
+
@cache.delete(all_cache_key(kind))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete(kind, key, version)
|
95
|
+
upsert(kind, { key: key, version: version, deleted: true })
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialized?
|
99
|
+
return true if @inited.value
|
100
|
+
|
101
|
+
if @cache.nil?
|
102
|
+
result = @core.initialized_internal?
|
103
|
+
else
|
104
|
+
result = @cache[inited_cache_key]
|
105
|
+
if result.nil?
|
106
|
+
result = @core.initialized_internal?
|
107
|
+
@cache[inited_cache_key] = result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
@inited.make_true if result
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
def stop
|
116
|
+
@core.stop
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# We use just one cache for 3 kinds of objects. Individual entities use a key like 'features:my-flag'.
|
122
|
+
def item_cache_key(kind, key)
|
123
|
+
kind[:namespace] + ":" + key.to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
# The result of a call to get_all_internal is cached using the "kind" object as a key.
|
127
|
+
def all_cache_key(kind)
|
128
|
+
kind
|
129
|
+
end
|
130
|
+
|
131
|
+
# The result of initialized_internal? is cached using this key.
|
132
|
+
def inited_cache_key
|
133
|
+
"$inited"
|
134
|
+
end
|
135
|
+
|
136
|
+
def item_if_not_deleted(item)
|
137
|
+
(item.nil? || item[:deleted]) ? nil : item
|
138
|
+
end
|
139
|
+
|
140
|
+
def items_if_not_deleted(items)
|
141
|
+
items.select { |key, item| !item[:deleted] }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# This module describes the methods that you must implement on your own object in order to
|
147
|
+
# use {CachingStoreWrapper}.
|
148
|
+
#
|
149
|
+
module FeatureStoreCore
|
150
|
+
#
|
151
|
+
# Initializes the store. This is the same as {LaunchDarkly::Interfaces::FeatureStore#init},
|
152
|
+
# but the wrapper will take care of updating the cache if caching is enabled.
|
153
|
+
#
|
154
|
+
# If possible, the store should update the entire data set atomically. If that is not possible,
|
155
|
+
# it should iterate through the outer hash and then the inner hash using the existing iteration
|
156
|
+
# order of those hashes (the SDK will ensure that the items were inserted into the hashes in
|
157
|
+
# the correct order), storing each item, and then delete any leftover items at the very end.
|
158
|
+
#
|
159
|
+
# @param all_data [Hash] a hash where each key is one of the data kind objects, and each
|
160
|
+
# value is in turn a hash of string keys to entities
|
161
|
+
# @return [void]
|
162
|
+
#
|
163
|
+
def init_internal(all_data)
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# Retrieves a single entity. This is the same as {LaunchDarkly::Interfaces::FeatureStore#get}
|
168
|
+
# except that 1. the wrapper will take care of filtering out deleted entities by checking the
|
169
|
+
# `:deleted` property, so you can just return exactly what was in the data store, and 2. the
|
170
|
+
# wrapper will take care of checking and updating the cache if caching is enabled.
|
171
|
+
#
|
172
|
+
# @param kind [Object] the kind of entity to get
|
173
|
+
# @param key [String] the unique key of the entity to get
|
174
|
+
# @return [Hash] the entity; nil if the key was not found
|
175
|
+
#
|
176
|
+
def get_internal(kind, key)
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Retrieves all entities of the specified kind. This is the same as {LaunchDarkly::Interfaces::FeatureStore#all}
|
181
|
+
# except that 1. the wrapper will take care of filtering out deleted entities by checking the
|
182
|
+
# `:deleted` property, so you can just return exactly what was in the data store, and 2. the
|
183
|
+
# wrapper will take care of checking and updating the cache if caching is enabled.
|
184
|
+
#
|
185
|
+
# @param kind [Object] the kind of entity to get
|
186
|
+
# @return [Hash] a hash where each key is the entity's `:key` property and each value
|
187
|
+
# is the entity
|
188
|
+
#
|
189
|
+
def get_all_internal(kind)
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# Attempts to add or update an entity. This is the same as {LaunchDarkly::Interfaces::FeatureStore#upsert}
|
194
|
+
# except that 1. the wrapper will take care of updating the cache if caching is enabled, and 2.
|
195
|
+
# the method is expected to return the final state of the entity (i.e. either the `item`
|
196
|
+
# parameter if the update succeeded, or the previously existing entity in the store if the
|
197
|
+
# update failed; this is used for the caching logic).
|
198
|
+
#
|
199
|
+
# Note that FeatureStoreCore does not have a `delete` method. This is because {CachingStoreWrapper}
|
200
|
+
# implements `delete` by simply calling `upsert` with an item whose `:deleted` property is true.
|
201
|
+
#
|
202
|
+
# @param kind [Object] the kind of entity to add or update
|
203
|
+
# @param item [Hash] the entity to add or update
|
204
|
+
# @return [Hash] the entity as it now exists in the store after the update
|
205
|
+
#
|
206
|
+
def upsert_internal(kind, item)
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Checks whether this store has been initialized. This is the same as
|
211
|
+
# {LaunchDarkly::Interfaces::FeatureStore#initialized?} except that there is less of a concern
|
212
|
+
# for efficiency, because the wrapper will use caching and memoization in order to call the method
|
213
|
+
# as little as possible.
|
214
|
+
#
|
215
|
+
# @return [Boolean] true if the store is in an initialized state
|
216
|
+
#
|
217
|
+
def initialized_internal?
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# Performs any necessary cleanup to shut down the store when the client is being shut down.
|
222
|
+
#
|
223
|
+
# @return [void]
|
224
|
+
#
|
225
|
+
def stop
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|