verdict 0.13.0 → 0.14.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 +3 -0
- data/lib/verdict/experiment.rb +22 -6
- data/lib/verdict/version.rb +1 -1
- data/test/experiment_test.rb +76 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f05448f375933cba0d01c10a4e302b684242297e779d96cd3bf45cff2f6fea74
|
4
|
+
data.tar.gz: d8782ab103377d758a550f36f16fa40cec2caa2662333a99d180aab637985126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14317430460d1fcb88748507d20fd8be9f0a271d434951c6deb4b89203eeb3a5675a7c88e5e1d79bd6404c275bc3a9a3b35558a09df51284c100613963c4ba51
|
7
|
+
data.tar.gz: 39ad70804de5c962785117525ccd668155efd228860735de13569316d22e6e3ed65e050842753c7283bc21c299ee237a00885686d6963a94e3130594f5c61b21
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
## v0.14.0
|
2
|
+
* Add optional experiment definition method `schedule_stop_new_assignment_timestamp` to support limiting experiment's assignment lifetime with another pre-determined time interval. It allows users to have an assignment cooldown period for stable analysis of the experiment results. Experiment's lifetime now becomes: start experiment -> stop new assignments -> end experiment.
|
3
|
+
|
1
4
|
## v0.13.0
|
2
5
|
|
3
6
|
* Add optional experiment definition methods `schedule_start_timestamp` and `schedule_end_timestamp` to support limiting experiment's lifetime in a pre-determined time interval.
|
data/lib/verdict/experiment.rb
CHANGED
@@ -52,9 +52,17 @@ class Verdict::Experiment
|
|
52
52
|
return self
|
53
53
|
end
|
54
54
|
|
55
|
-
# Optional: Together with the "
|
56
|
-
# the given time interval.
|
57
|
-
#
|
55
|
+
# Optional: Together with the "end_timestamp" and "stop_new_assignment_timestamp", limits the experiment run timeline within
|
56
|
+
# the given time interval.
|
57
|
+
#
|
58
|
+
# Timestamps definitions:
|
59
|
+
# start_timestamp: Experiment's start time. No assignments are made i.e. switch will return nil before this timestamp.
|
60
|
+
# stop_new_assignment_timestamp: Experiment's new assignment stop time. No new assignments are made
|
61
|
+
# i.e. switch returns nil for new assignments but the existing assignments are preserved.
|
62
|
+
# end_timestamp: Experiment's end time. No assignments are made i.e. switch returns nil after this timestamp.
|
63
|
+
#
|
64
|
+
# Experiment run timeline:
|
65
|
+
# start_timestamp -> (new assignments occur) -> stop_new_assignment_timestamp -> (no new assignments occur) -> end_timestamp
|
58
66
|
def schedule_start_timestamp(timestamp)
|
59
67
|
@schedule_start_timestamp = timestamp
|
60
68
|
end
|
@@ -63,6 +71,10 @@ class Verdict::Experiment
|
|
63
71
|
@schedule_end_timestamp = timestamp
|
64
72
|
end
|
65
73
|
|
74
|
+
def schedule_stop_new_assignment_timestamp(timestamp)
|
75
|
+
@schedule_stop_new_assignment_timestamp = timestamp
|
76
|
+
end
|
77
|
+
|
66
78
|
def rollout_percentage(percentage, rollout_group_name = :enabled)
|
67
79
|
groups(Verdict::Segmenters::RolloutSegmenter) do
|
68
80
|
group rollout_group_name, percentage
|
@@ -136,7 +148,7 @@ class Verdict::Experiment
|
|
136
148
|
subject_identifier = retrieve_subject_identifier(subject)
|
137
149
|
assignment = if previous_assignment
|
138
150
|
previous_assignment
|
139
|
-
elsif subject_qualifies?(subject, context)
|
151
|
+
elsif subject_qualifies?(subject, context) && is_make_new_assignments?
|
140
152
|
group = segmenter.assign(subject_identifier, subject, context)
|
141
153
|
subject_assignment(subject, group, nil, group.nil?)
|
142
154
|
else
|
@@ -268,12 +280,16 @@ class Verdict::Experiment
|
|
268
280
|
private
|
269
281
|
|
270
282
|
def is_scheduled?
|
271
|
-
if @schedule_start_timestamp
|
283
|
+
if @schedule_start_timestamp && @schedule_start_timestamp > Time.now
|
272
284
|
return false
|
273
285
|
end
|
274
|
-
if @schedule_end_timestamp
|
286
|
+
if @schedule_end_timestamp && @schedule_end_timestamp <= Time.now
|
275
287
|
return false
|
276
288
|
end
|
277
289
|
return true
|
278
290
|
end
|
291
|
+
|
292
|
+
def is_make_new_assignments?
|
293
|
+
return !(@schedule_stop_new_assignment_timestamp && @schedule_stop_new_assignment_timestamp <= Time.now)
|
294
|
+
end
|
279
295
|
end
|
data/lib/verdict/version.rb
CHANGED
data/test/experiment_test.rb
CHANGED
@@ -565,6 +565,82 @@ class ExperimentTest < Minitest::Test
|
|
565
565
|
end
|
566
566
|
end
|
567
567
|
|
568
|
+
def test_is_stop_new_assignments
|
569
|
+
e = Verdict::Experiment.new('test') do
|
570
|
+
groups do
|
571
|
+
group :a, :half
|
572
|
+
group :b, :half
|
573
|
+
end
|
574
|
+
schedule_stop_new_assignment_timestamp Time.new(2020, 1, 15)
|
575
|
+
end
|
576
|
+
|
577
|
+
# new assignments stopped after the stop timestamp
|
578
|
+
Timecop.freeze(Time.new(2020, 1, 16)) do
|
579
|
+
assert !e.send(:is_make_new_assignments?)
|
580
|
+
assert_nil e.switch(1)
|
581
|
+
end
|
582
|
+
# new assignments didn't stop before the stop timestamp
|
583
|
+
Timecop.freeze(Time.new(2020, 1, 3)) do
|
584
|
+
assert e.send(:is_make_new_assignments?)
|
585
|
+
assert :a, e.switch(2)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def test_switch_preserves_old_assignments_after_stop_new_assignments_timestamp
|
590
|
+
e = Verdict::Experiment.new('test') do
|
591
|
+
groups do
|
592
|
+
group :a, :half
|
593
|
+
group :b, :half
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
assert_equal :a, e.switch(1)
|
598
|
+
|
599
|
+
e.schedule_stop_new_assignment_timestamp Time.new(2020, 4, 15)
|
600
|
+
|
601
|
+
# switch respects to stop new assignment timestamp, old assignment preserves, new assignment returns nil
|
602
|
+
Timecop.freeze(Time.new(2020, 4, 16)) do
|
603
|
+
assert !e.send(:is_make_new_assignments?)
|
604
|
+
# old assignment stay the same
|
605
|
+
assert_equal :a, e.switch(1)
|
606
|
+
# new assignment returns nil
|
607
|
+
assert_nil e.switch(2)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def test_schedule_start_timestamp_and_stop_new_assignemnt_timestamp_are_inclusive_but_end_timestamp_is_exclusive
|
612
|
+
e = Verdict::Experiment.new('test') do
|
613
|
+
groups do
|
614
|
+
group :a, :half
|
615
|
+
group :b, :half
|
616
|
+
end
|
617
|
+
|
618
|
+
schedule_start_timestamp Time.new(2020, 1, 1)
|
619
|
+
schedule_stop_new_assignment_timestamp Time.new(2020, 1, 15)
|
620
|
+
schedule_end_timestamp Time.new(2020, 1, 31)
|
621
|
+
end
|
622
|
+
|
623
|
+
# start_timestamp is included
|
624
|
+
Timecop.freeze(Time.new(2020, 1, 1)) do
|
625
|
+
assert e.send(:is_scheduled?)
|
626
|
+
assert_equal :a, e.switch(1)
|
627
|
+
end
|
628
|
+
|
629
|
+
# stop_new_assignment_timestamp is included
|
630
|
+
Timecop.freeze(Time.new(2020, 1, 15)) do
|
631
|
+
assert !e.send(:is_make_new_assignments?)
|
632
|
+
# old assignment preserved
|
633
|
+
assert_equal :a, e.switch(1)
|
634
|
+
# new assignment returns nil
|
635
|
+
assert_nil e.switch(2)
|
636
|
+
end
|
637
|
+
|
638
|
+
# end_timestamp is excluded
|
639
|
+
Timecop.freeze(Time.new(2020, 1, 31)) do
|
640
|
+
assert !e.send(:is_scheduled?)
|
641
|
+
assert_nil e.switch(1)
|
642
|
+
end
|
643
|
+
end
|
568
644
|
private
|
569
645
|
|
570
646
|
def redis
|
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.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|