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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef6e75a9737694176e2d31ed58fe999222189c2f86a30099fd34a2892756b570
4
- data.tar.gz: 5010e420ef5cc2bfd79223ba1ef91526fe429d8040272cc2eaa0cf072a05246e
3
+ metadata.gz: 23482b6ebd56ecd7bdb03d318c4a507e8d9ca6b3a70ace6c63a52349855f1666
4
+ data.tar.gz: fe4b75a65e3daca4c40cfead80d1c9ff266e40faa4af99b912c8f5116b7f5cae
5
5
  SHA512:
6
- metadata.gz: a4389fc051d535c43bd81198023ab8dc38c26eca9805f751f235b3dc08fddcb9d123c70c513751b118c24470303448577047802f86b23f81e18a926218231b8c
7
- data.tar.gz: 1b478420092e7fd734e14d11f28a7bfcca6c019a56d8eb4d193b331993a7596e770a25ca0f63e82a648292ca331feffcc18523bbe3799d1db077a98bc1ddbc6d
6
+ metadata.gz: a403a445b44731fccaddb181fcda9acbd2b138adad07fadde5d8c3b0f97fd97c02a3873256672b1dc2d6756b7471b6575345b9e525efee27c8c25c7d89d3bf3c
7
+ data.tar.gz: 3d52f3c2517c91a132c327ba57a695f008f611b82ff17d6bee86cd23628102c4b6d9f70ad62768929eb986d46ce566c215c6f676e4c0883029c24d422ad32c7d
@@ -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
 
@@ -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 in identifier is not implemented for experiment @{handle.inspect}."
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("#{@key_prefix}#{scope}", key)
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("#{@key_prefix}#{scope}", key, value)
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("#{@key_prefix}#{scope}", key)
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 generate_scope_key(scope)
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
@@ -1,3 +1,3 @@
1
1
  module Verdict
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -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.9.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-06-05 00:00:00.000000000 Z
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
- rubyforge_project:
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