verdict 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/verdict/experiment.rb +5 -1
- data/lib/verdict/storage/base_storage.rb +4 -0
- data/lib/verdict/storage/redis_storage.rb +21 -4
- data/lib/verdict/version.rb +1 -1
- data/test/experiment_test.rb +28 -0
- data/test/storage/redis_storage_test.rb +17 -4
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23482b6ebd56ecd7bdb03d318c4a507e8d9ca6b3a70ace6c63a52349855f1666
|
4
|
+
data.tar.gz: fe4b75a65e3daca4c40cfead80d1c9ff266e40faa4af99b912c8f5116b7f5cae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a403a445b44731fccaddb181fcda9acbd2b138adad07fadde5d8c3b0f97fd97c02a3873256672b1dc2d6756b7471b6575345b9e525efee27c8c25c7d89d3bf3c
|
7
|
+
data.tar.gz: 3d52f3c2517c91a132c327ba57a695f008f611b82ff17d6bee86cd23628102c4b6d9f70ad62768929eb986d46ce566c215c6f676e4c0883029c24d422ad32c7d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
## v0.10.0
|
2
|
+
|
3
|
+
* Add `Experiment#cleanup` to remove stored redis hashes.
|
4
|
+
* Fix typo in `Experiment#fetch_subject` error message.
|
5
|
+
|
1
6
|
## v0.9.0
|
2
7
|
**This version has breaking changes**
|
8
|
+
|
3
9
|
* Eagerly load experiment definitions when booting Rails, so that multi-threaded applications do not face a race-condition when populating experiments.
|
4
10
|
* Fixes deprecated `assert_equal` tests that return nil.
|
5
11
|
|
data/lib/verdict/experiment.rb
CHANGED
@@ -162,6 +162,10 @@ class Verdict::Experiment
|
|
162
162
|
assignment
|
163
163
|
end
|
164
164
|
|
165
|
+
def cleanup
|
166
|
+
@storage.cleanup(handle.to_s)
|
167
|
+
end
|
168
|
+
|
165
169
|
def remove_subject_assignment(subject)
|
166
170
|
@storage.remove_assignment(self, subject)
|
167
171
|
end
|
@@ -207,7 +211,7 @@ class Verdict::Experiment
|
|
207
211
|
end
|
208
212
|
|
209
213
|
def fetch_subject(subject_identifier)
|
210
|
-
raise NotImplementedError, "Fetching subjects based
|
214
|
+
raise NotImplementedError, "Fetching subjects based on identifier is not implemented for experiment #{@handle.inspect}."
|
211
215
|
end
|
212
216
|
|
213
217
|
def disqualify_empty_identifier?
|
@@ -45,6 +45,10 @@ module Verdict
|
|
45
45
|
set(experiment.handle.to_s, 'started_at', timestamp.utc.strftime('%FT%TZ'))
|
46
46
|
end
|
47
47
|
|
48
|
+
def cleanup(_scope)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
48
52
|
protected
|
49
53
|
# Retrieves a key in a given scope from storage.
|
50
54
|
# - The scope and key are both provided as string.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Verdict
|
2
2
|
module Storage
|
3
3
|
class RedisStorage < BaseStorage
|
4
|
+
PAGE_SIZE = 50
|
5
|
+
|
4
6
|
attr_accessor :redis, :key_prefix
|
5
7
|
|
6
8
|
def initialize(redis = nil, options = {})
|
@@ -9,28 +11,43 @@ module Verdict
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def get(scope, key)
|
12
|
-
redis.hget(
|
14
|
+
redis.hget(scope_key(scope), key)
|
13
15
|
rescue ::Redis::BaseError => e
|
14
16
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
15
17
|
end
|
16
18
|
|
17
19
|
def set(scope, key, value)
|
18
|
-
redis.hset(
|
20
|
+
redis.hset(scope_key(scope), key, value)
|
19
21
|
rescue ::Redis::BaseError => e
|
20
22
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
21
23
|
end
|
22
24
|
|
23
25
|
def remove(scope, key)
|
24
|
-
redis.hdel(
|
26
|
+
redis.hdel(scope_key(scope), key)
|
27
|
+
rescue ::Redis::BaseError => e
|
28
|
+
raise Verdict::StorageError, "Redis error: #{e.message}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def cleanup(scope)
|
32
|
+
clear(scope)
|
33
|
+
redis.del(scope_key(scope))
|
25
34
|
rescue ::Redis::BaseError => e
|
26
35
|
raise Verdict::StorageError, "Redis error: #{e.message}"
|
27
36
|
end
|
28
37
|
|
29
38
|
private
|
30
39
|
|
31
|
-
def
|
40
|
+
def scope_key(scope)
|
32
41
|
"#{@key_prefix}#{scope}"
|
33
42
|
end
|
43
|
+
|
44
|
+
def clear(scope, cursor: 0)
|
45
|
+
cursor, results = redis.hscan(scope_key(scope), cursor, count: PAGE_SIZE)
|
46
|
+
results.map(&:first).each do |key|
|
47
|
+
remove(scope, key)
|
48
|
+
end
|
49
|
+
clear(scope, cursor: cursor) unless cursor.to_i.zero?
|
50
|
+
end
|
34
51
|
end
|
35
52
|
end
|
36
53
|
end
|
data/lib/verdict/version.rb
CHANGED
data/test/experiment_test.rb
CHANGED
@@ -422,4 +422,32 @@ class ExperimentTest < Minitest::Test
|
|
422
422
|
|
423
423
|
assert_kind_of Verdict::Storage::MockStorage, e.storage
|
424
424
|
end
|
425
|
+
|
426
|
+
def test_cleanup
|
427
|
+
redis = ::Redis.new(host: REDIS_HOST, port: REDIS_PORT)
|
428
|
+
storage = Verdict::Storage::RedisStorage.new(redis)
|
429
|
+
experiment = Verdict::Experiment.new(:cleanup_test) do
|
430
|
+
groups { group :all, 100 }
|
431
|
+
storage storage, store_unqualified: true
|
432
|
+
end
|
433
|
+
|
434
|
+
experiment.assign("something")
|
435
|
+
assert_operator redis, :exists, "experiments/cleanup_test"
|
436
|
+
|
437
|
+
experiment.cleanup
|
438
|
+
refute_operator redis, :exists, "experiments/cleanup_test"
|
439
|
+
ensure
|
440
|
+
redis.del("experiments/cleanup_test")
|
441
|
+
end
|
442
|
+
|
443
|
+
def test_cleanup_without_redis
|
444
|
+
experiment = Verdict::Experiment.new(:cleanup_test) do
|
445
|
+
groups { group :all, 100 }
|
446
|
+
end
|
447
|
+
|
448
|
+
assert_raises(NotImplementedError) do
|
449
|
+
experiment.assign("something")
|
450
|
+
experiment.cleanup
|
451
|
+
end
|
452
|
+
end
|
425
453
|
end
|
@@ -16,10 +16,6 @@ class RedisStorageTest < Minitest::Test
|
|
16
16
|
@redis.del('experiments/redis_storage')
|
17
17
|
end
|
18
18
|
|
19
|
-
def experiment_key
|
20
|
-
'experiments/redis_storage'
|
21
|
-
end
|
22
|
-
|
23
19
|
def test_store_and_retrieve_qualified_assignment
|
24
20
|
refute @redis.hexists(experiment_key, 'assignment_subject_1')
|
25
21
|
|
@@ -74,4 +70,21 @@ class RedisStorageTest < Minitest::Test
|
|
74
70
|
assert @redis.hexists(experiment_key, "started_at")
|
75
71
|
assert_equal a, @experiment.started_at
|
76
72
|
end
|
73
|
+
|
74
|
+
def test_cleanup
|
75
|
+
1000.times do |n|
|
76
|
+
@experiment.assign("something_#{n}")
|
77
|
+
end
|
78
|
+
|
79
|
+
assert_operator @redis, :exists, experiment_key
|
80
|
+
|
81
|
+
@storage.cleanup(:redis_storage)
|
82
|
+
refute_operator @redis, :exists, experiment_key
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def experiment_key
|
88
|
+
"experiments/redis_storage"
|
89
|
+
end
|
77
90
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verdict
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -172,8 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
172
|
- !ruby/object:Gem::Version
|
173
173
|
version: '0'
|
174
174
|
requirements: []
|
175
|
-
|
176
|
-
rubygems_version: 2.7.6
|
175
|
+
rubygems_version: 3.0.3
|
177
176
|
signing_key:
|
178
177
|
specification_version: 4
|
179
178
|
summary: A library to centrally define experiments for your application, and collect
|