launchdarkly-server-sdk 6.2.5 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/lib/ldclient-rb/config.rb +203 -43
- data/lib/ldclient-rb/context.rb +487 -0
- data/lib/ldclient-rb/evaluation_detail.rb +85 -26
- data/lib/ldclient-rb/events.rb +185 -146
- data/lib/ldclient-rb/flags_state.rb +25 -14
- data/lib/ldclient-rb/impl/big_segments.rb +117 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +145 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
- data/lib/ldclient-rb/impl/evaluator.rb +428 -132
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
- data/lib/ldclient-rb/impl/event_sender.rb +6 -6
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +78 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +92 -28
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +165 -32
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
- data/lib/ldclient-rb/impl/model/clause.rb +39 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +213 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +126 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
- data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
- data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +62 -1
- data/lib/ldclient-rb/in_memory_store.rb +2 -2
- data/lib/ldclient-rb/integrations/consul.rb +9 -2
- data/lib/ldclient-rb/integrations/dynamodb.rb +47 -2
- data/lib/ldclient-rb/integrations/file_data.rb +108 -0
- data/lib/ldclient-rb/integrations/redis.rb +43 -3
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +594 -0
- data/lib/ldclient-rb/integrations/test_data.rb +213 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +14 -9
- data/lib/ldclient-rb/integrations.rb +2 -51
- data/lib/ldclient-rb/interfaces.rb +151 -1
- data/lib/ldclient-rb/ldclient.rb +175 -133
- data/lib/ldclient-rb/memoized_value.rb +1 -1
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
- data/lib/ldclient-rb/polling.rb +22 -41
- data/lib/ldclient-rb/reference.rb +274 -0
- data/lib/ldclient-rb/requestor.rb +7 -7
- data/lib/ldclient-rb/stream.rb +9 -9
- data/lib/ldclient-rb/util.rb +11 -17
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +2 -4
- metadata +49 -23
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/file_data_source.rb +0 -314
- data/lib/ldclient-rb/impl/event_factory.rb +0 -126
- data/lib/ldclient-rb/newrelic.rb +0 -17
- data/lib/ldclient-rb/redis_store.rb +0 -88
- data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'ldclient-rb/impl/integrations/test_data/test_data_source'
|
2
|
+
require 'ldclient-rb/impl/model/feature_flag'
|
3
|
+
require 'ldclient-rb/impl/model/segment'
|
4
|
+
require 'ldclient-rb/integrations/test_data/flag_builder'
|
5
|
+
|
6
|
+
require 'concurrent/atomics'
|
7
|
+
|
8
|
+
module LaunchDarkly
|
9
|
+
module Integrations
|
10
|
+
#
|
11
|
+
# A mechanism for providing dynamically updatable feature flag state in a simplified form to an SDK
|
12
|
+
# client in test scenarios.
|
13
|
+
#
|
14
|
+
# Unlike {LaunchDarkly::Integrations::FileData}, this mechanism does not use any external resources. It
|
15
|
+
# provides only the data that the application has put into it using the {#update} method.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# td = LaunchDarkly::Integrations::TestData.data_source
|
19
|
+
# td.update(td.flag("flag-key-1").variation_for_all(true))
|
20
|
+
# config = LaunchDarkly::Config.new(data_source: td)
|
21
|
+
# client = LaunchDarkly::LDClient.new('sdkKey', config)
|
22
|
+
# # flags can be updated at any time:
|
23
|
+
# td.update(td.flag("flag-key-2")
|
24
|
+
# .variation_for_key("user", some-user-key", true)
|
25
|
+
# .fallthrough_variation(false))
|
26
|
+
#
|
27
|
+
# The above example uses a simple boolean flag, but more complex configurations are possible using
|
28
|
+
# the methods of the {FlagBuilder} that is returned by {#flag}. {FlagBuilder}
|
29
|
+
# supports many of the ways a flag can be configured on the LaunchDarkly dashboard, but does not
|
30
|
+
# currently support 1. rule operators other than "in" and "not in", or 2. percentage rollouts.
|
31
|
+
#
|
32
|
+
# If the same `TestData` instance is used to configure multiple `LDClient` instances,
|
33
|
+
# any changes made to the data will propagate to all of the `LDClient`s.
|
34
|
+
#
|
35
|
+
# @since 6.3.0
|
36
|
+
#
|
37
|
+
class TestData
|
38
|
+
# Creates a new instance of the test data source.
|
39
|
+
#
|
40
|
+
# @return [TestData] a new configurable test data source
|
41
|
+
def self.data_source
|
42
|
+
self.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# @private
|
46
|
+
def initialize
|
47
|
+
@flag_builders = Hash.new
|
48
|
+
@current_flags = Hash.new
|
49
|
+
@current_segments = Hash.new
|
50
|
+
@instances = Array.new
|
51
|
+
@instances_lock = Concurrent::ReadWriteLock.new
|
52
|
+
@lock = Concurrent::ReadWriteLock.new
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Called internally by the SDK to determine what arguments to pass to call
|
57
|
+
# You do not need to call this method.
|
58
|
+
#
|
59
|
+
# @private
|
60
|
+
def arity
|
61
|
+
2
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Called internally by the SDK to associate this test data source with an {@code LDClient} instance.
|
66
|
+
# You do not need to call this method.
|
67
|
+
#
|
68
|
+
# @private
|
69
|
+
def call(_, config)
|
70
|
+
impl = LaunchDarkly::Impl::Integrations::TestData::TestDataSource.new(config.feature_store, self)
|
71
|
+
@instances_lock.with_write_lock { @instances.push(impl) }
|
72
|
+
impl
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Creates or copies a {FlagBuilder} for building a test flag configuration.
|
77
|
+
#
|
78
|
+
# If this flag key has already been defined in this `TestData` instance, then the builder
|
79
|
+
# starts with the same configuration that was last provided for this flag.
|
80
|
+
#
|
81
|
+
# Otherwise, it starts with a new default configuration in which the flag has `true` and
|
82
|
+
# `false` variations, is `true` for all contexts when targeting is turned on and
|
83
|
+
# `false` otherwise, and currently has targeting turned on. You can change any of those
|
84
|
+
# properties, and provide more complex behavior, using the {FlagBuilder} methods.
|
85
|
+
#
|
86
|
+
# Once you have set the desired configuration, pass the builder to {#update}.
|
87
|
+
#
|
88
|
+
# @param key [String] the flag key
|
89
|
+
# @return [FlagBuilder] a flag configuration builder
|
90
|
+
#
|
91
|
+
def flag(key)
|
92
|
+
existing_builder = @lock.with_read_lock { @flag_builders[key] }
|
93
|
+
if existing_builder.nil? then
|
94
|
+
FlagBuilder.new(key).boolean_flag
|
95
|
+
else
|
96
|
+
existing_builder.clone
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Updates the test data with the specified flag configuration.
|
102
|
+
#
|
103
|
+
# This has the same effect as if a flag were added or modified on the LaunchDarkly dashboard.
|
104
|
+
# It immediately propagates the flag change to any `LDClient` instance(s) that you have
|
105
|
+
# already configured to use this `TestData`. If no `LDClient` has been started yet,
|
106
|
+
# it simply adds this flag to the test data which will be provided to any `LDClient` that
|
107
|
+
# you subsequently configure.
|
108
|
+
#
|
109
|
+
# Any subsequent changes to this {FlagBuilder} instance do not affect the test data,
|
110
|
+
# unless you call {#update} again.
|
111
|
+
#
|
112
|
+
# @param flag_builder [FlagBuilder] a flag configuration builder
|
113
|
+
# @return [TestData] the TestData instance
|
114
|
+
#
|
115
|
+
def update(flag_builder)
|
116
|
+
new_flag = nil
|
117
|
+
@lock.with_write_lock do
|
118
|
+
@flag_builders[flag_builder.key] = flag_builder
|
119
|
+
version = 0
|
120
|
+
flag_key = flag_builder.key.to_sym
|
121
|
+
if @current_flags[flag_key] then
|
122
|
+
version = @current_flags[flag_key][:version]
|
123
|
+
end
|
124
|
+
new_flag = Impl::Model.deserialize(FEATURES, flag_builder.build(version+1))
|
125
|
+
@current_flags[flag_key] = new_flag
|
126
|
+
end
|
127
|
+
update_item(FEATURES, new_flag)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Copies a full feature flag data model object into the test data.
|
133
|
+
#
|
134
|
+
# It immediately propagates the flag change to any `LDClient` instance(s) that you have already
|
135
|
+
# configured to use this `TestData`. If no `LDClient` has been started yet, it simply adds
|
136
|
+
# this flag to the test data which will be provided to any LDClient that you subsequently
|
137
|
+
# configure.
|
138
|
+
#
|
139
|
+
# Use this method if you need to use advanced flag configuration properties that are not supported by
|
140
|
+
# the simplified {FlagBuilder} API. Otherwise it is recommended to use the regular {flag}/{update}
|
141
|
+
# mechanism to avoid dependencies on details of the data model.
|
142
|
+
#
|
143
|
+
# You cannot make incremental changes with {flag}/{update} to a flag that has been added in this way;
|
144
|
+
# you can only replace it with an entirely new flag configuration.
|
145
|
+
#
|
146
|
+
# @param flag [Hash] the flag configuration
|
147
|
+
# @return [TestData] the TestData instance
|
148
|
+
#
|
149
|
+
def use_preconfigured_flag(flag)
|
150
|
+
use_preconfigured_item(FEATURES, flag, @current_flags)
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Copies a full segment data model object into the test data.
|
155
|
+
#
|
156
|
+
# It immediately propagates the change to any `LDClient` instance(s) that you have already
|
157
|
+
# configured to use this `TestData`. If no `LDClient` has been started yet, it simply adds
|
158
|
+
# this segment to the test data which will be provided to any LDClient that you subsequently
|
159
|
+
# configure.
|
160
|
+
#
|
161
|
+
# This method is currently the only way to inject segment data, since there is no builder
|
162
|
+
# API for segments. It is mainly intended for the SDK's own tests of segment functionality,
|
163
|
+
# since application tests that need to produce a desired evaluation state could do so more easily
|
164
|
+
# by just setting flag values.
|
165
|
+
#
|
166
|
+
# @param segment [Hash] the segment configuration
|
167
|
+
# @return [TestData] the TestData instance
|
168
|
+
#
|
169
|
+
def use_preconfigured_segment(segment)
|
170
|
+
use_preconfigured_item(SEGMENTS, segment, @current_segments)
|
171
|
+
end
|
172
|
+
|
173
|
+
private def use_preconfigured_item(kind, item, current)
|
174
|
+
item = Impl::Model.deserialize(kind, item)
|
175
|
+
key = item.key.to_sym
|
176
|
+
@lock.with_write_lock do
|
177
|
+
old_item = current[key]
|
178
|
+
unless old_item.nil? then
|
179
|
+
data = item.as_json
|
180
|
+
data[:version] = old_item.version + 1
|
181
|
+
item = Impl::Model.deserialize(kind, data)
|
182
|
+
end
|
183
|
+
current[key] = item
|
184
|
+
end
|
185
|
+
update_item(kind, item)
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
private def update_item(kind, item)
|
190
|
+
@instances_lock.with_read_lock do
|
191
|
+
@instances.each do | instance |
|
192
|
+
instance.upsert(kind, item)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# @private
|
198
|
+
def make_init_data
|
199
|
+
@lock.with_read_lock do
|
200
|
+
{
|
201
|
+
FEATURES => @current_flags.clone,
|
202
|
+
SEGMENTS => @current_segments.clone,
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# @private
|
208
|
+
def closed_instance(instance)
|
209
|
+
@instances_lock.with_write_lock { @instances.delete(instance) }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -4,6 +4,11 @@ require "ldclient-rb/expiring_cache"
|
|
4
4
|
|
5
5
|
module LaunchDarkly
|
6
6
|
module Integrations
|
7
|
+
#
|
8
|
+
# Support code that may be helpful in creating integrations.
|
9
|
+
#
|
10
|
+
# @since 5.5.0
|
11
|
+
#
|
7
12
|
module Util
|
8
13
|
#
|
9
14
|
# CachingStoreWrapper is a partial implementation of the {LaunchDarkly::Interfaces::FeatureStore}
|
@@ -17,7 +22,7 @@ module LaunchDarkly
|
|
17
22
|
#
|
18
23
|
class CachingStoreWrapper
|
19
24
|
include LaunchDarkly::Interfaces::FeatureStore
|
20
|
-
|
25
|
+
|
21
26
|
#
|
22
27
|
# Creates a new store wrapper instance.
|
23
28
|
#
|
@@ -44,7 +49,7 @@ module LaunchDarkly
|
|
44
49
|
@core.init_internal(all_data)
|
45
50
|
@inited.make_true
|
46
51
|
|
47
|
-
|
52
|
+
unless @cache.nil?
|
48
53
|
@cache.clear
|
49
54
|
all_data.each do |kind, items|
|
50
55
|
@cache[kind] = items_if_not_deleted(items)
|
@@ -56,15 +61,15 @@ module LaunchDarkly
|
|
56
61
|
end
|
57
62
|
|
58
63
|
def get(kind, key)
|
59
|
-
|
64
|
+
unless @cache.nil?
|
60
65
|
cache_key = item_cache_key(kind, key)
|
61
66
|
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])
|
67
|
+
return item_if_not_deleted(cached[0]) unless cached.nil?
|
63
68
|
end
|
64
69
|
|
65
70
|
item = @core.get_internal(kind, key)
|
66
71
|
|
67
|
-
|
72
|
+
unless @cache.nil?
|
68
73
|
@cache[cache_key] = [item]
|
69
74
|
end
|
70
75
|
|
@@ -72,20 +77,20 @@ module LaunchDarkly
|
|
72
77
|
end
|
73
78
|
|
74
79
|
def all(kind)
|
75
|
-
|
80
|
+
unless @cache.nil?
|
76
81
|
items = @cache[all_cache_key(kind)]
|
77
|
-
return items
|
82
|
+
return items unless items.nil?
|
78
83
|
end
|
79
84
|
|
80
85
|
items = items_if_not_deleted(@core.get_all_internal(kind))
|
81
|
-
@cache[all_cache_key(kind)] = items
|
86
|
+
@cache[all_cache_key(kind)] = items unless @cache.nil?
|
82
87
|
items
|
83
88
|
end
|
84
89
|
|
85
90
|
def upsert(kind, item)
|
86
91
|
new_state = @core.upsert_internal(kind, item)
|
87
92
|
|
88
|
-
|
93
|
+
unless @cache.nil?
|
89
94
|
@cache[item_cache_key(kind, item[:key])] = [new_state]
|
90
95
|
@cache.delete(all_cache_key(kind))
|
91
96
|
end
|
@@ -1,55 +1,6 @@
|
|
1
1
|
require "ldclient-rb/integrations/consul"
|
2
2
|
require "ldclient-rb/integrations/dynamodb"
|
3
|
+
require "ldclient-rb/integrations/file_data"
|
3
4
|
require "ldclient-rb/integrations/redis"
|
5
|
+
require "ldclient-rb/integrations/test_data"
|
4
6
|
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
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "observer"
|
1
2
|
|
2
3
|
module LaunchDarkly
|
3
4
|
#
|
@@ -120,7 +121,8 @@ module LaunchDarkly
|
|
120
121
|
#
|
121
122
|
# The client has its own standard implementation, which uses either a streaming connection or
|
122
123
|
# polling depending on your configuration. Normally you will not need to use another one
|
123
|
-
# except for testing purposes.
|
124
|
+
# except for testing purposes. Two such test fixtures are {LaunchDarkly::Integrations::FileData}
|
125
|
+
# and {LaunchDarkly::Integrations::TestData}.
|
124
126
|
#
|
125
127
|
module DataSource
|
126
128
|
#
|
@@ -149,5 +151,153 @@ module LaunchDarkly
|
|
149
151
|
def stop
|
150
152
|
end
|
151
153
|
end
|
154
|
+
|
155
|
+
module BigSegmentStore
|
156
|
+
#
|
157
|
+
# Returns information about the overall state of the store. This method will be called only
|
158
|
+
# when the SDK needs the latest state, so it should not be cached.
|
159
|
+
#
|
160
|
+
# @return [BigSegmentStoreMetadata]
|
161
|
+
#
|
162
|
+
def get_metadata
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Queries the store for a snapshot of the current segment state for a specific context.
|
167
|
+
#
|
168
|
+
# The context_hash is a base64-encoded string produced by hashing the context key as defined by
|
169
|
+
# the Big Segments specification; the store implementation does not need to know the details
|
170
|
+
# of how this is done, because it deals only with already-hashed keys, but the string can be
|
171
|
+
# assumed to only contain characters that are valid in base64.
|
172
|
+
#
|
173
|
+
# The return value should be either a Hash, or nil if the context is not referenced in any big
|
174
|
+
# segments. Each key in the Hash is a "segment reference", which is how segments are
|
175
|
+
# identified in Big Segment data. This string is not identical to the segment key-- the SDK
|
176
|
+
# will add other information. The store implementation should not be concerned with the
|
177
|
+
# format of the string. Each value in the Hash is true if the context is explicitly included in
|
178
|
+
# the segment, false if the context is explicitly excluded from the segment-- and is not also
|
179
|
+
# explicitly included (that is, if both an include and an exclude existed in the data, the
|
180
|
+
# include would take precedence). If the context's status in a particular segment is undefined,
|
181
|
+
# there should be no key or value for that segment.
|
182
|
+
#
|
183
|
+
# This Hash may be cached by the SDK, so it should not be modified after it is created. It
|
184
|
+
# is a snapshot of the segment membership state at one point in time.
|
185
|
+
#
|
186
|
+
# @param context_hash [String]
|
187
|
+
# @return [Hash] true/false values for Big Segments that reference this context
|
188
|
+
#
|
189
|
+
def get_membership(context_hash)
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# Performs any necessary cleanup to shut down the store when the client is being shut down.
|
194
|
+
#
|
195
|
+
# @return [void]
|
196
|
+
#
|
197
|
+
def stop
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Values returned by {BigSegmentStore#get_metadata}.
|
203
|
+
#
|
204
|
+
class BigSegmentStoreMetadata
|
205
|
+
def initialize(last_up_to_date)
|
206
|
+
@last_up_to_date = last_up_to_date
|
207
|
+
end
|
208
|
+
|
209
|
+
# The Unix epoch millisecond timestamp of the last update to the {BigSegmentStore}. It is
|
210
|
+
# nil if the store has never been updated.
|
211
|
+
#
|
212
|
+
# @return [Integer|nil]
|
213
|
+
attr_reader :last_up_to_date
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Information about the status of a Big Segment store, provided by {BigSegmentStoreStatusProvider}.
|
218
|
+
#
|
219
|
+
# Big Segments are a specific type of segments. For more information, read the LaunchDarkly
|
220
|
+
# documentation: https://docs.launchdarkly.com/home/users/big-segments
|
221
|
+
#
|
222
|
+
class BigSegmentStoreStatus
|
223
|
+
def initialize(available, stale)
|
224
|
+
@available = available
|
225
|
+
@stale = stale
|
226
|
+
end
|
227
|
+
|
228
|
+
# True if the Big Segment store is able to respond to queries, so that the SDK can evaluate
|
229
|
+
# whether a context is in a segment or not.
|
230
|
+
#
|
231
|
+
# If this property is false, the store is not able to make queries (for instance, it may not have
|
232
|
+
# a valid database connection). In this case, the SDK will treat any reference to a Big Segment
|
233
|
+
# as if no contexts are included in that segment. Also, the {EvaluationReason} associated with
|
234
|
+
# with any flag evaluation that references a Big Segment when the store is not available will
|
235
|
+
# have a `big_segments_status` of `STORE_ERROR`.
|
236
|
+
#
|
237
|
+
# @return [Boolean]
|
238
|
+
attr_reader :available
|
239
|
+
|
240
|
+
# True if the Big Segment store is available, but has not been updated within the amount of time
|
241
|
+
# specified by {BigSegmentsConfig#stale_after}.
|
242
|
+
#
|
243
|
+
# This may indicate that the LaunchDarkly Relay Proxy, which populates the store, has stopped
|
244
|
+
# running or has become unable to receive fresh data from LaunchDarkly. Any feature flag
|
245
|
+
# evaluations that reference a Big Segment will be using the last known data, which may be out
|
246
|
+
# of date. Also, the {EvaluationReason} associated with those evaluations will have a
|
247
|
+
# `big_segments_status` of `STALE`.
|
248
|
+
#
|
249
|
+
# @return [Boolean]
|
250
|
+
attr_reader :stale
|
251
|
+
|
252
|
+
def ==(other)
|
253
|
+
self.available == other.available && self.stale == other.stale
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
# An interface for querying the status of a Big Segment store.
|
259
|
+
#
|
260
|
+
# The Big Segment store is the component that receives information about Big Segments, normally
|
261
|
+
# from a database populated by the LaunchDarkly Relay Proxy. Big Segments are a specific type
|
262
|
+
# of segments. For more information, read the LaunchDarkly documentation:
|
263
|
+
# https://docs.launchdarkly.com/home/users/big-segments
|
264
|
+
#
|
265
|
+
# An implementation of this interface is returned by {LDClient#big_segment_store_status_provider}.
|
266
|
+
# Application code never needs to implement this interface.
|
267
|
+
#
|
268
|
+
# There are two ways to interact with the status. One is to simply get the current status; if its
|
269
|
+
# `available` property is true, then the SDK is able to evaluate context membership in Big Segments,
|
270
|
+
# and the `stale`` property indicates whether the data might be out of date.
|
271
|
+
#
|
272
|
+
# The other way is to subscribe to status change notifications. Applications may wish to know if
|
273
|
+
# there is an outage in the Big Segment store, or if it has become stale (the Relay Proxy has
|
274
|
+
# stopped updating it with new data), since then flag evaluations that reference a Big Segment
|
275
|
+
# might return incorrect values. To allow finding out about status changes as soon as possible,
|
276
|
+
# `BigSegmentStoreStatusProvider` mixes in Ruby's
|
277
|
+
# [Observable](https://docs.ruby-lang.org/en/2.5.0/Observable.html) module to provide standard
|
278
|
+
# methods such as `add_observer`. Observers will be called with a new {BigSegmentStoreStatus}
|
279
|
+
# value whenever the status changes.
|
280
|
+
#
|
281
|
+
# @example Getting the current status
|
282
|
+
# status = client.big_segment_store_status_provider.status
|
283
|
+
#
|
284
|
+
# @example Subscribing to status notifications
|
285
|
+
# client.big_segment_store_status_provider.add_observer(self, :big_segments_status_changed)
|
286
|
+
#
|
287
|
+
# def big_segments_status_changed(new_status)
|
288
|
+
# puts "Big segment store status is now: #{new_status}"
|
289
|
+
# end
|
290
|
+
#
|
291
|
+
module BigSegmentStoreStatusProvider
|
292
|
+
include Observable
|
293
|
+
#
|
294
|
+
# Gets the current status of the store, if known.
|
295
|
+
#
|
296
|
+
# @return [BigSegmentStoreStatus] the status, or nil if the SDK has not yet queried the Big
|
297
|
+
# Segment store status
|
298
|
+
#
|
299
|
+
def status
|
300
|
+
end
|
301
|
+
end
|
152
302
|
end
|
153
303
|
end
|