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
data/lib/ldclient-rb/stream.rb
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
require "ldclient-rb/impl/model/serialization"
|
|
2
|
-
|
|
3
|
-
require "concurrent/atomics"
|
|
4
|
-
require "json"
|
|
5
|
-
require "ld-eventsource"
|
|
6
|
-
|
|
7
|
-
module LaunchDarkly
|
|
8
|
-
# @private
|
|
9
|
-
PUT = :put
|
|
10
|
-
# @private
|
|
11
|
-
PATCH = :patch
|
|
12
|
-
# @private
|
|
13
|
-
DELETE = :delete
|
|
14
|
-
# @private
|
|
15
|
-
READ_TIMEOUT_SECONDS = 300 # 5 minutes; the stream should send a ping every 3 minutes
|
|
16
|
-
|
|
17
|
-
# @private
|
|
18
|
-
KEY_PATHS = {
|
|
19
|
-
FEATURES => "/flags/",
|
|
20
|
-
SEGMENTS => "/segments/",
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
# @private
|
|
24
|
-
class StreamProcessor
|
|
25
|
-
def initialize(sdk_key, config, diagnostic_accumulator = nil)
|
|
26
|
-
@sdk_key = sdk_key
|
|
27
|
-
@config = config
|
|
28
|
-
@data_source_update_sink = config.data_source_update_sink
|
|
29
|
-
@feature_store = config.feature_store
|
|
30
|
-
@initialized = Concurrent::AtomicBoolean.new(false)
|
|
31
|
-
@started = Concurrent::AtomicBoolean.new(false)
|
|
32
|
-
@stopped = Concurrent::AtomicBoolean.new(false)
|
|
33
|
-
@ready = Concurrent::Event.new
|
|
34
|
-
@connection_attempt_start_time = 0
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def initialized?
|
|
38
|
-
@initialized.value
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def start
|
|
42
|
-
return @ready unless @started.make_true
|
|
43
|
-
|
|
44
|
-
@config.logger.info { "[LDClient] Initializing stream connection" }
|
|
45
|
-
|
|
46
|
-
headers = Impl::Util.default_http_headers(@sdk_key, @config)
|
|
47
|
-
opts = {
|
|
48
|
-
headers: headers,
|
|
49
|
-
read_timeout: READ_TIMEOUT_SECONDS,
|
|
50
|
-
logger: @config.logger,
|
|
51
|
-
socket_factory: @config.socket_factory,
|
|
52
|
-
reconnect_time: @config.initial_reconnect_delay,
|
|
53
|
-
}
|
|
54
|
-
log_connection_started
|
|
55
|
-
|
|
56
|
-
uri = Util.add_payload_filter_key(@config.stream_uri + "/all", @config)
|
|
57
|
-
@es = SSE::Client.new(uri, **opts) do |conn|
|
|
58
|
-
conn.on_event { |event| process_message(event) }
|
|
59
|
-
conn.on_error { |err|
|
|
60
|
-
log_connection_result(false)
|
|
61
|
-
case err
|
|
62
|
-
when SSE::Errors::HTTPStatusError
|
|
63
|
-
status = err.status
|
|
64
|
-
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
65
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, status, nil, Time.now)
|
|
66
|
-
message = Util.http_error_message(status, "streaming connection", "will retry")
|
|
67
|
-
@config.logger.error { "[LDClient] #{message}" }
|
|
68
|
-
|
|
69
|
-
if Util.http_error_recoverable?(status)
|
|
70
|
-
@data_source_update_sink&.update_status(
|
|
71
|
-
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
|
72
|
-
error_info
|
|
73
|
-
)
|
|
74
|
-
else
|
|
75
|
-
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
|
76
|
-
stop_with_error_info error_info
|
|
77
|
-
end
|
|
78
|
-
when SSE::Errors::HTTPContentTypeError, SSE::Errors::HTTPProxyError, SSE::Errors::ReadTimeoutError
|
|
79
|
-
@data_source_update_sink&.update_status(
|
|
80
|
-
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
|
81
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::NETWORK_ERROR, 0, err.to_s, Time.now)
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
else
|
|
85
|
-
@data_source_update_sink&.update_status(
|
|
86
|
-
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
|
87
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, err.to_s, Time.now)
|
|
88
|
-
)
|
|
89
|
-
end
|
|
90
|
-
}
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
@ready
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def stop
|
|
97
|
-
stop_with_error_info
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
private
|
|
101
|
-
|
|
102
|
-
#
|
|
103
|
-
# @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info
|
|
104
|
-
#
|
|
105
|
-
def stop_with_error_info(error_info = nil)
|
|
106
|
-
if @stopped.make_true
|
|
107
|
-
@es.close
|
|
108
|
-
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info)
|
|
109
|
-
@config.logger.info { "[LDClient] Stream connection stopped" }
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
# The original implementation of this class relied on the feature store
|
|
115
|
-
# directly, which we are trying to move away from. Customers who might have
|
|
116
|
-
# instantiated this directly for some reason wouldn't know they have to set
|
|
117
|
-
# the config's sink manually, so we have to fall back to the store if the
|
|
118
|
-
# sink isn't present.
|
|
119
|
-
#
|
|
120
|
-
# The next major release should be able to simplify this structure and
|
|
121
|
-
# remove the need for fall back to the data store because the update sink
|
|
122
|
-
# should always be present.
|
|
123
|
-
#
|
|
124
|
-
def update_sink_or_data_store
|
|
125
|
-
@data_source_update_sink || @feature_store
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def process_message(message)
|
|
129
|
-
log_connection_result(true)
|
|
130
|
-
method = message.type
|
|
131
|
-
@config.logger.debug { "[LDClient] Stream received #{method} message: #{message.data}" }
|
|
132
|
-
|
|
133
|
-
begin
|
|
134
|
-
if method == PUT
|
|
135
|
-
message = JSON.parse(message.data, symbolize_names: true)
|
|
136
|
-
all_data = Impl::Model.make_all_store_data(message[:data], @config.logger)
|
|
137
|
-
update_sink_or_data_store.init(all_data)
|
|
138
|
-
@initialized.make_true
|
|
139
|
-
@config.logger.info { "[LDClient] Stream initialized" }
|
|
140
|
-
@ready.set
|
|
141
|
-
elsif method == PATCH
|
|
142
|
-
data = JSON.parse(message.data, symbolize_names: true)
|
|
143
|
-
for kind in [FEATURES, SEGMENTS]
|
|
144
|
-
key = key_for_path(kind, data[:path])
|
|
145
|
-
if key
|
|
146
|
-
item = Impl::Model.deserialize(kind, data[:data], @config.logger)
|
|
147
|
-
update_sink_or_data_store.upsert(kind, item)
|
|
148
|
-
break
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
elsif method == DELETE
|
|
152
|
-
data = JSON.parse(message.data, symbolize_names: true)
|
|
153
|
-
for kind in [FEATURES, SEGMENTS]
|
|
154
|
-
key = key_for_path(kind, data[:path])
|
|
155
|
-
if key
|
|
156
|
-
update_sink_or_data_store.delete(kind, key, data[:version])
|
|
157
|
-
break
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
else
|
|
161
|
-
@config.logger.warn { "[LDClient] Unknown message received: #{method}" }
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil)
|
|
165
|
-
rescue JSON::ParserError => e
|
|
166
|
-
@config.logger.error { "[LDClient] JSON parsing failed for method #{method}. Ignoring event." }
|
|
167
|
-
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
|
168
|
-
LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA,
|
|
169
|
-
0,
|
|
170
|
-
e.to_s,
|
|
171
|
-
Time.now
|
|
172
|
-
)
|
|
173
|
-
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
|
|
174
|
-
|
|
175
|
-
# Re-raise the exception so the SSE implementation can catch it and restart the stream.
|
|
176
|
-
raise
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def key_for_path(kind, path)
|
|
181
|
-
path.start_with?(KEY_PATHS[kind]) ? path[KEY_PATHS[kind].length..-1] : nil
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def log_connection_started
|
|
185
|
-
@connection_attempt_start_time = Impl::Util::current_time_millis
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def log_connection_result(is_success)
|
|
189
|
-
if !@diagnostic_accumulator.nil? && @connection_attempt_start_time > 0
|
|
190
|
-
@diagnostic_accumulator.record_stream_init(@connection_attempt_start_time, !is_success,
|
|
191
|
-
Impl::Util::current_time_millis - @connection_attempt_start_time)
|
|
192
|
-
@connection_attempt_start_time = 0
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
end
|