verdict 0.13.0 → 0.14.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: ec6df6bdd38dc947ff1072e1dce844ab4d3ddd522701c8de578a35b4c9c51be8
4
- data.tar.gz: e6b62b2c579a363f37526079389bea4cb16eb28ed7573dec2d3fa8f8c74ee4ed
3
+ metadata.gz: f05448f375933cba0d01c10a4e302b684242297e779d96cd3bf45cff2f6fea74
4
+ data.tar.gz: d8782ab103377d758a550f36f16fa40cec2caa2662333a99d180aab637985126
5
5
  SHA512:
6
- metadata.gz: 1399fa37c4575888ffb83aea54469784db11ceef1ebc99f5c1fa5de75b936f460b1f34dc717c28f4f19a6e969c3a85a3faad022119113cd94f754ad61b4fdb85
7
- data.tar.gz: 7f1836b8d23ca79a4338f0a4f4ed244ebe7bdbd8b9f03529f2cb00e110c280b1033e112928da900d805112eb0036e0f74278ea021514016541accf01d8c3308c
6
+ metadata.gz: 14317430460d1fcb88748507d20fd8be9f0a271d434951c6deb4b89203eeb3a5675a7c88e5e1d79bd6404c275bc3a9a3b35558a09df51284c100613963c4ba51
7
+ data.tar.gz: 39ad70804de5c962785117525ccd668155efd228860735de13569316d22e6e3ed65e050842753c7283bc21c299ee237a00885686d6963a94e3130594f5c61b21
@@ -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.
@@ -52,9 +52,17 @@ class Verdict::Experiment
52
52
  return self
53
53
  end
54
54
 
55
- # Optional: Together with the "end timestamp", limits the experiment run timeline within
56
- # the given time interval. When experiment is not scheduled, subject switch returns nil.
57
- # This is useful when the experimenter requires the experiment to run in a strict timeline.
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 and @schedule_start_timestamp > Time.now
283
+ if @schedule_start_timestamp && @schedule_start_timestamp > Time.now
272
284
  return false
273
285
  end
274
- if @schedule_end_timestamp and @schedule_end_timestamp < Time.now
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
@@ -1,3 +1,3 @@
1
1
  module Verdict
2
- VERSION = "0.13.0"
2
+ VERSION = "0.14.0"
3
3
  end
@@ -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.13.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-03 00:00:00.000000000 Z
11
+ date: 2020-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest