verdict 0.9.0 → 0.10.0
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/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
|