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 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