ldclient-rb 4.0.0 → 5.0.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 +11 -4
- data/CHANGELOG.md +9 -0
- data/README.md +7 -3
- data/ldclient-rb.gemspec +6 -24
- data/lib/ldclient-rb.rb +1 -0
- data/lib/ldclient-rb/events.rb +13 -7
- data/lib/ldclient-rb/ldclient.rb +29 -12
- data/lib/ldclient-rb/polling.rb +15 -8
- data/lib/ldclient-rb/requestor.rb +10 -14
- data/lib/ldclient-rb/stream.rb +26 -27
- data/lib/ldclient-rb/user_filter.rb +1 -0
- data/lib/ldclient-rb/util.rb +18 -0
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/sse_client.rb +4 -0
- data/lib/sse_client/backoff.rb +38 -0
- data/lib/sse_client/sse_client.rb +162 -0
- data/lib/sse_client/sse_events.rb +67 -0
- data/lib/sse_client/streaming_http.rb +195 -0
- data/spec/events_spec.rb +30 -3
- data/spec/ldclient_spec.rb +1 -10
- data/spec/polling_spec.rb +89 -0
- data/spec/sse_client/sse_client_spec.rb +139 -0
- data/spec/sse_client/sse_events_spec.rb +100 -0
- data/spec/sse_client/sse_shared.rb +82 -0
- data/spec/sse_client/streaming_http_spec.rb +263 -0
- metadata +24 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 743c38bedf7c153667c5a38fcc2deaffd557f09d
|
4
|
+
data.tar.gz: c352ed4f8c412e2c19edcbe94d5813a2478ddade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6add89667ba084d57d7e49b52ced7ff279aa491aabc2285354353ce6b6f8393f14c04bef18062b1762ab6cf0bc97b27060009e589f78523fe8acaba9c9ff4a3d
|
7
|
+
data.tar.gz: 0a46454ca002c7fc35d15fc2343097727552cdc81ef517d96032befe77a451ddbd9e3f99b048cec5e132480c4ebb124685a8cdd282c6982f1c40844ceef7313b
|
data/.circleci/config.yml
CHANGED
@@ -8,7 +8,8 @@ workflows:
|
|
8
8
|
- test-2.2
|
9
9
|
- test-2.3
|
10
10
|
- test-2.4
|
11
|
-
- test-
|
11
|
+
- test-2.5
|
12
|
+
- test-jruby-9.2
|
12
13
|
|
13
14
|
ruby-docker-template: &ruby-docker-template
|
14
15
|
steps:
|
@@ -17,6 +18,7 @@ ruby-docker-template: &ruby-docker-template
|
|
17
18
|
if [[ $CIRCLE_JOB == test-jruby* ]]; then
|
18
19
|
gem install jruby-openssl; # required by bundler, no effect on Ruby MRI
|
19
20
|
fi
|
21
|
+
- run: ruby -v
|
20
22
|
- run: gem install bundler
|
21
23
|
- run: bundle install
|
22
24
|
- run: mkdir ./rspec
|
@@ -40,9 +42,14 @@ jobs:
|
|
40
42
|
test-2.4:
|
41
43
|
<<: *ruby-docker-template
|
42
44
|
docker:
|
43
|
-
- image: circleci/ruby:2.4.
|
45
|
+
- image: circleci/ruby:2.4.4-stretch
|
44
46
|
- image: redis
|
45
|
-
test-
|
47
|
+
test-2.5:
|
48
|
+
<<: *ruby-docker-template
|
49
|
+
docker:
|
50
|
+
- image: circleci/ruby:2.5.1-stretch
|
51
|
+
- image: redis
|
52
|
+
test-jruby-9.2:
|
46
53
|
<<: *ruby-docker-template
|
47
54
|
docker:
|
48
55
|
- image: circleci/jruby:9-jdk
|
@@ -54,7 +61,7 @@ jobs:
|
|
54
61
|
machine:
|
55
62
|
image: circleci/classic:latest
|
56
63
|
environment:
|
57
|
-
- RUBIES: "
|
64
|
+
- RUBIES: "jruby-9.1.17.0"
|
58
65
|
steps:
|
59
66
|
- run: sudo apt-get -q update
|
60
67
|
- run: sudo apt-get -qy install redis-server
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
All notable changes to the LaunchDarkly Ruby SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [5.0.0] - 2018-06-26
|
6
|
+
### Changed:
|
7
|
+
- The client no longer uses Celluloid for streaming I/O. Instead, it uses [socketry](https://github.com/socketry/socketry).
|
8
|
+
- The client now treats most HTTP 4xx errors as unrecoverable: that is, after receiving such an error, it will not make any more HTTP requests for the lifetime of the client instance, in effect taking the client offline. This is because such errors indicate either a configuration problem (invalid SDK key) or a bug, which is not likely to resolve without a restart or an upgrade. This does not apply if the error is 400, 408, 429, or any 5xx error.
|
9
|
+
- During initialization, if the client receives any of the unrecoverable errors described above, the client constructor will return immediately; previously it would continue waiting until a timeout. The `initialized?` method will return false in this case.
|
10
|
+
|
11
|
+
### Removed:
|
12
|
+
- The SDK no longer supports Ruby versions below 2.2.6, or JRuby below 9.1.16.
|
13
|
+
|
5
14
|
## [4.0.0] - 2018-05-10
|
6
15
|
|
7
16
|
### Changed:
|
data/README.md
CHANGED
@@ -7,15 +7,19 @@ LaunchDarkly SDK for Ruby
|
|
7
7
|
[![Test Coverage](https://codeclimate.com/github/launchdarkly/ruby-client/badges/coverage.svg)](https://codeclimate.com/github/launchdarkly/ruby-client/coverage)
|
8
8
|
[![security](https://hakiri.io/github/launchdarkly/ruby-client/master.svg)](https://hakiri.io/github/launchdarkly/ruby-client/master)
|
9
9
|
|
10
|
+
Supported Ruby versions
|
11
|
+
-----------------------
|
12
|
+
|
13
|
+
This version of the LaunchDarkly SDK has a minimum Ruby version of 2.2.6, or 9.1.6 for JRuby.
|
14
|
+
|
10
15
|
Quick setup
|
11
16
|
-----------
|
12
17
|
|
13
18
|
0. Install the Ruby SDK with `gem`
|
14
19
|
|
15
20
|
```shell
|
16
|
-
gem install ldclient-rb
|
21
|
+
gem install ldclient-rb
|
17
22
|
```
|
18
|
-
Note: The `--prerelease` flag is there to satisfy the dependency of celluloid 0.18pre which we have tested extensively and have found stable in our use case. Unfortunately, the upstream provider has not promoted this version to stable yet. See [here](https://github.com/celluloid/celluloid/issues/762) This is not required for use in a Gemfile.
|
19
23
|
|
20
24
|
1. Require the LaunchDarkly client:
|
21
25
|
|
@@ -79,7 +83,7 @@ Note that this gem will automatically switch to using the Rails logger it is det
|
|
79
83
|
|
80
84
|
HTTPS proxy
|
81
85
|
------------
|
82
|
-
The Ruby SDK uses Faraday to handle
|
86
|
+
The Ruby SDK uses Faraday and Socketry to handle its network traffic. Both of these provide built-in support for the use of an HTTPS proxy. If the HTTPS_PROXY environment variable is present then the SDK will proxy all network requests through the URL provided.
|
83
87
|
|
84
88
|
How to set the HTTPS_PROXY environment variable on Mac/Linux systems:
|
85
89
|
```
|
data/ldclient-rb.gemspec
CHANGED
@@ -26,36 +26,18 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "codeclimate-test-reporter", "~> 0"
|
27
27
|
spec.add_development_dependency "redis", "~> 3.3.5"
|
28
28
|
spec.add_development_dependency "connection_pool", ">= 2.1.2"
|
29
|
-
|
30
|
-
|
31
|
-
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
32
|
-
else
|
33
|
-
spec.add_development_dependency "rake", "12.1.0"
|
34
|
-
# higher versions of rake fail to install in JRuby 1.7
|
35
|
-
end
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
30
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
36
31
|
spec.add_development_dependency "timecop", "~> 0.9.1"
|
37
32
|
|
38
33
|
spec.add_runtime_dependency "json", [">= 1.8", "< 3"]
|
39
|
-
|
40
|
-
|
41
|
-
spec.add_runtime_dependency "faraday-http-cache", [">= 1.3.0", "< 3"]
|
42
|
-
else
|
43
|
-
spec.add_runtime_dependency "faraday", [">= 0.9", "< 0.14.0"]
|
44
|
-
spec.add_runtime_dependency "faraday-http-cache", [">= 1.3.0", "< 2"]
|
45
|
-
end
|
34
|
+
spec.add_runtime_dependency "faraday", [">= 0.9", "< 2"]
|
35
|
+
spec.add_runtime_dependency "faraday-http-cache", [">= 1.3.0", "< 3"]
|
46
36
|
spec.add_runtime_dependency "semantic", "~> 1.6.0"
|
47
37
|
spec.add_runtime_dependency "thread_safe", "~> 0.3"
|
48
38
|
spec.add_runtime_dependency "net-http-persistent", "~> 2.9"
|
49
39
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0.4"
|
50
40
|
spec.add_runtime_dependency "hashdiff", "~> 0.2"
|
51
|
-
spec.add_runtime_dependency "
|
52
|
-
spec.add_runtime_dependency "
|
53
|
-
|
54
|
-
if RUBY_VERSION >= "2.2.2"
|
55
|
-
spec.add_runtime_dependency "nio4r", "< 3" # for maximum ruby version compatibility.
|
56
|
-
else
|
57
|
-
spec.add_runtime_dependency "nio4r", "~> 1.1" # for maximum ruby version compatibility.
|
58
|
-
end
|
59
|
-
|
60
|
-
spec.add_runtime_dependency "waitutil", "0.2"
|
41
|
+
spec.add_runtime_dependency "http_tools", '~> 0.4.5'
|
42
|
+
spec.add_runtime_dependency "socketry", "~> 0.5.1"
|
61
43
|
end
|
data/lib/ldclient-rb.rb
CHANGED
data/lib/ldclient-rb/events.rb
CHANGED
@@ -222,17 +222,24 @@ module LaunchDarkly
|
|
222
222
|
if !payload.events.empty? || !payload.summary.counters.empty?
|
223
223
|
# If all available worker threads are busy, success will be false and no job will be queued.
|
224
224
|
success = flush_workers.post do
|
225
|
-
|
226
|
-
|
225
|
+
begin
|
226
|
+
resp = EventPayloadSendTask.new.run(@sdk_key, @config, @client, payload, @formatter)
|
227
|
+
handle_response(resp) if !resp.nil?
|
228
|
+
rescue => e
|
229
|
+
@config.logger.warn { "[LDClient] Unexpected error in event processor: #{e.inspect}. \nTrace: #{e.backtrace}" }
|
230
|
+
end
|
227
231
|
end
|
228
232
|
buffer.clear if success # Reset our internal state, these events now belong to the flush worker
|
229
233
|
end
|
230
234
|
end
|
231
235
|
|
232
236
|
def handle_response(res)
|
233
|
-
if res.status
|
234
|
-
|
235
|
-
@
|
237
|
+
if res.status >= 400
|
238
|
+
message = Util.http_error_message(res.status, "event delivery", "some events were dropped")
|
239
|
+
@config.logger.error { "[LDClient] #{message}" }
|
240
|
+
if !Util.http_error_recoverable?(res.status)
|
241
|
+
@disabled.value = true
|
242
|
+
end
|
236
243
|
else
|
237
244
|
if !res.headers.nil? && res.headers.has_key?("Date")
|
238
245
|
begin
|
@@ -309,8 +316,7 @@ module LaunchDarkly
|
|
309
316
|
next
|
310
317
|
end
|
311
318
|
if res.status < 200 || res.status >= 300
|
312
|
-
|
313
|
-
if res.status >= 500
|
319
|
+
if Util.http_error_recoverable?(res.status)
|
314
320
|
next
|
315
321
|
end
|
316
322
|
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
require "concurrent/atomics"
|
1
2
|
require "digest/sha1"
|
2
3
|
require "logger"
|
3
4
|
require "benchmark"
|
4
|
-
require "waitutil"
|
5
5
|
require "json"
|
6
6
|
require "openssl"
|
7
7
|
|
@@ -41,7 +41,9 @@ module LaunchDarkly
|
|
41
41
|
|
42
42
|
requestor = Requestor.new(sdk_key, config)
|
43
43
|
|
44
|
-
if
|
44
|
+
if @config.offline?
|
45
|
+
@update_processor = NullUpdateProcessor.new
|
46
|
+
else
|
45
47
|
if @config.update_processor.nil?
|
46
48
|
if @config.stream?
|
47
49
|
@update_processor = StreamProcessor.new(sdk_key, config, requestor)
|
@@ -53,16 +55,15 @@ module LaunchDarkly
|
|
53
55
|
else
|
54
56
|
@update_processor = @config.update_processor
|
55
57
|
end
|
56
|
-
@update_processor.start
|
57
58
|
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
rescue WaitUtil::TimeoutError
|
60
|
+
ready = @update_processor.start
|
61
|
+
if wait_for_sec > 0
|
62
|
+
ok = ready.wait(wait_for_sec)
|
63
|
+
if !ok
|
65
64
|
@config.logger.error { "[LDClient] Timeout encountered waiting for LaunchDarkly client initialization" }
|
65
|
+
elsif !@update_processor.initialized?
|
66
|
+
@config.logger.error { "[LDClient] LaunchDarkly client initialization failed" }
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
@@ -220,9 +221,7 @@ module LaunchDarkly
|
|
220
221
|
# @return [void]
|
221
222
|
def close
|
222
223
|
@config.logger.info { "[LDClient] Closing LaunchDarkly client..." }
|
223
|
-
|
224
|
-
@update_processor.stop
|
225
|
-
end
|
224
|
+
@update_processor.stop
|
226
225
|
@event_processor.stop
|
227
226
|
@store.stop
|
228
227
|
end
|
@@ -255,4 +254,22 @@ module LaunchDarkly
|
|
255
254
|
|
256
255
|
private :evaluate, :log_exception, :sanitize_user, :make_feature_event
|
257
256
|
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Used internally when the client is offline.
|
260
|
+
#
|
261
|
+
class NullUpdateProcessor
|
262
|
+
def start
|
263
|
+
e = Concurrent::Event.new
|
264
|
+
e.set
|
265
|
+
e
|
266
|
+
end
|
267
|
+
|
268
|
+
def initialized?
|
269
|
+
true
|
270
|
+
end
|
271
|
+
|
272
|
+
def stop
|
273
|
+
end
|
274
|
+
end
|
258
275
|
end
|
data/lib/ldclient-rb/polling.rb
CHANGED
@@ -9,6 +9,7 @@ module LaunchDarkly
|
|
9
9
|
@initialized = Concurrent::AtomicBoolean.new(false)
|
10
10
|
@started = Concurrent::AtomicBoolean.new(false)
|
11
11
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
12
|
+
@ready = Concurrent::Event.new
|
12
13
|
end
|
13
14
|
|
14
15
|
def initialized?
|
@@ -16,9 +17,10 @@ module LaunchDarkly
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def start
|
19
|
-
return unless @started.make_true
|
20
|
+
return @ready unless @started.make_true
|
20
21
|
@config.logger.info { "[LDClient] Initializing polling connection" }
|
21
22
|
create_worker
|
23
|
+
@ready
|
22
24
|
end
|
23
25
|
|
24
26
|
def stop
|
@@ -39,6 +41,7 @@ module LaunchDarkly
|
|
39
41
|
})
|
40
42
|
if @initialized.make_true
|
41
43
|
@config.logger.info { "[LDClient] Polling connection initialized" }
|
44
|
+
@ready.set
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
@@ -47,20 +50,24 @@ module LaunchDarkly
|
|
47
50
|
@worker = Thread.new do
|
48
51
|
@config.logger.debug { "[LDClient] Starting polling worker" }
|
49
52
|
while !@stopped.value do
|
53
|
+
started_at = Time.now
|
50
54
|
begin
|
51
|
-
started_at = Time.now
|
52
55
|
poll
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
rescue UnexpectedResponseError => e
|
57
|
+
message = Util.http_error_message(e.status, "polling request", "will retry")
|
58
|
+
@config.logger.error { "[LDClient] #{message}" };
|
59
|
+
if !Util.http_error_recoverable?(e.status)
|
60
|
+
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
61
|
+
stop
|
56
62
|
end
|
57
|
-
rescue InvalidSDKKeyError
|
58
|
-
@config.logger.error { "[LDClient] Received 401 error, no further polling requests will be made since SDK key is invalid" };
|
59
|
-
stop
|
60
63
|
rescue StandardError => exn
|
61
64
|
@config.logger.error { "[LDClient] Exception while polling: #{exn.inspect}" }
|
62
65
|
# TODO: log_exception(__method__.to_s, exn)
|
63
66
|
end
|
67
|
+
delta = @config.poll_interval - (Time.now - started_at)
|
68
|
+
if delta > 0
|
69
|
+
sleep(delta)
|
70
|
+
end
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
@@ -4,7 +4,14 @@ require "faraday/http_cache"
|
|
4
4
|
|
5
5
|
module LaunchDarkly
|
6
6
|
|
7
|
-
class
|
7
|
+
class UnexpectedResponseError < StandardError
|
8
|
+
def initialize(status)
|
9
|
+
@status = status
|
10
|
+
end
|
11
|
+
|
12
|
+
def status
|
13
|
+
@status
|
14
|
+
end
|
8
15
|
end
|
9
16
|
|
10
17
|
class Requestor
|
@@ -13,7 +20,7 @@ module LaunchDarkly
|
|
13
20
|
@config = config
|
14
21
|
@client = Faraday.new do |builder|
|
15
22
|
builder.use :http_cache, store: @config.cache_store
|
16
|
-
|
23
|
+
|
17
24
|
builder.adapter :net_http_persistent
|
18
25
|
end
|
19
26
|
end
|
@@ -44,19 +51,8 @@ module LaunchDarkly
|
|
44
51
|
|
45
52
|
@config.logger.debug { "[LDClient] Got response from uri: #{uri}\n\tstatus code: #{res.status}\n\theaders: #{res.headers}\n\tbody: #{res.body}" }
|
46
53
|
|
47
|
-
if res.status == 401
|
48
|
-
@config.logger.error { "[LDClient] Invalid SDK key" }
|
49
|
-
raise InvalidSDKKeyError
|
50
|
-
end
|
51
|
-
|
52
|
-
if res.status == 404
|
53
|
-
@config.logger.error { "[LDClient] Resource not found" }
|
54
|
-
return nil
|
55
|
-
end
|
56
|
-
|
57
54
|
if res.status < 200 || res.status >= 300
|
58
|
-
|
59
|
-
return nil
|
55
|
+
raise UnexpectedResponseError.new(res.status)
|
60
56
|
end
|
61
57
|
|
62
58
|
JSON.parse(res.body, symbolize_names: true)
|
data/lib/ldclient-rb/stream.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "concurrent/atomics"
|
2
2
|
require "json"
|
3
|
-
require "
|
3
|
+
require "sse_client"
|
4
4
|
|
5
5
|
module LaunchDarkly
|
6
6
|
PUT = :put
|
@@ -24,6 +24,7 @@ module LaunchDarkly
|
|
24
24
|
@initialized = Concurrent::AtomicBoolean.new(false)
|
25
25
|
@started = Concurrent::AtomicBoolean.new(false)
|
26
26
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
27
|
+
@ready = Concurrent::Event.new
|
27
28
|
end
|
28
29
|
|
29
30
|
def initialized?
|
@@ -31,37 +32,34 @@ module LaunchDarkly
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def start
|
34
|
-
return unless @started.make_true
|
35
|
+
return @ready unless @started.make_true
|
35
36
|
|
36
37
|
@config.logger.info { "[LDClient] Initializing stream connection" }
|
37
38
|
|
38
|
-
headers =
|
39
|
-
{
|
39
|
+
headers = {
|
40
40
|
'Authorization' => @sdk_key,
|
41
41
|
'User-Agent' => 'RubyClient/' + LaunchDarkly::VERSION
|
42
42
|
}
|
43
|
-
opts = {
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
opts = {
|
44
|
+
headers: headers,
|
45
|
+
proxy: @config.proxy,
|
46
|
+
read_timeout: READ_TIMEOUT_SECONDS,
|
47
|
+
logger: @config.logger
|
48
|
+
}
|
49
|
+
@es = SSE::SSEClient.new(@config.stream_uri + "/all", opts) do |conn|
|
50
|
+
conn.on_event { |event| process_message(event, event.type) }
|
50
51
|
conn.on_error { |err|
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
status = err[:status_code]
|
53
|
+
message = Util.http_error_message(status, "streaming connection", "will retry")
|
54
|
+
@config.logger.error { "[LDClient] #{message}" }
|
55
|
+
if !Util.http_error_recoverable?(status)
|
56
|
+
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
54
57
|
stop
|
55
58
|
end
|
56
59
|
}
|
57
60
|
end
|
58
|
-
|
59
|
-
|
60
|
-
def stop
|
61
|
-
if @stopped.make_true
|
62
|
-
@es.close
|
63
|
-
@config.logger.info { "[LDClient] Stream connection stopped" }
|
64
|
-
end
|
61
|
+
|
62
|
+
@ready
|
65
63
|
end
|
66
64
|
|
67
65
|
def stop
|
@@ -83,21 +81,22 @@ module LaunchDarkly
|
|
83
81
|
})
|
84
82
|
@initialized.make_true
|
85
83
|
@config.logger.info { "[LDClient] Stream initialized" }
|
84
|
+
@ready.set
|
86
85
|
elsif method == PATCH
|
87
|
-
|
86
|
+
data = JSON.parse(message.data, symbolize_names: true)
|
88
87
|
for kind in [FEATURES, SEGMENTS]
|
89
|
-
key = key_for_path(kind,
|
88
|
+
key = key_for_path(kind, data[:path])
|
90
89
|
if key
|
91
|
-
@feature_store.upsert(kind,
|
90
|
+
@feature_store.upsert(kind, data[:data])
|
92
91
|
break
|
93
92
|
end
|
94
93
|
end
|
95
94
|
elsif method == DELETE
|
96
|
-
|
95
|
+
data = JSON.parse(message.data, symbolize_names: true)
|
97
96
|
for kind in [FEATURES, SEGMENTS]
|
98
|
-
key = key_for_path(kind,
|
97
|
+
key = key_for_path(kind, data[:path])
|
99
98
|
if key
|
100
|
-
@feature_store.delete(kind, key,
|
99
|
+
@feature_store.delete(kind, key, data[:version])
|
101
100
|
break
|
102
101
|
end
|
103
102
|
end
|