@@ -35,7 +35,7 @@ def initialize(logger)
35
35
@bucket_seed = HASH_SEED
36
36
end
37
37
38
- def bucket ( project_config , experiment , bucketing_id , user_id , decide_reasons = nil )
38
+ def bucket ( project_config , experiment , bucketing_id , user_id )
39
39
# Determines ID of variation to be shown for a given experiment key and user ID.
40
40
#
41
41
# project_config - Instance of ProjectConfig
@@ -44,7 +44,9 @@ def bucket(project_config, experiment, bucketing_id, user_id, decide_reasons = n
44
44
# user_id - String ID for user.
45
45
#
46
46
# Returns variation in which visitor with ID user_id has been placed. Nil if no variation.
47
- return nil if experiment . nil?
47
+ return nil , [ ] if experiment . nil?
48
+
49
+ decide_reasons = [ ]
48
50
49
51
# check if experiment is in a group; if so, check if user is bucketed into specified experiment
50
52
# this will not affect evaluation of rollout rules.
@@ -55,72 +57,77 @@ def bucket(project_config, experiment, bucketing_id, user_id, decide_reasons = n
55
57
group = project_config . group_id_map . fetch ( group_id )
56
58
if Helpers ::Group . random_policy? ( group )
57
59
traffic_allocations = group . fetch ( 'trafficAllocation' )
58
- bucketed_experiment_id = find_bucket ( bucketing_id , user_id , group_id , traffic_allocations )
60
+ bucketed_experiment_id , find_bucket_reasons = find_bucket ( bucketing_id , user_id , group_id , traffic_allocations )
61
+ decide_reasons . push ( *find_bucket_reasons )
62
+
59
63
# return if the user is not bucketed into any experiment
60
64
unless bucketed_experiment_id
61
65
message = "User '#{ user_id } ' is in no experiment."
62
66
@logger . log ( Logger ::INFO , message )
63
- decide_reasons & .push ( message )
64
- return nil
67
+ decide_reasons . push ( message )
68
+ return nil , decide_reasons
65
69
end
66
70
67
71
# return if the user is bucketed into a different experiment than the one specified
68
72
if bucketed_experiment_id != experiment_id
69
73
message = "User '#{ user_id } ' is not in experiment '#{ experiment_key } ' of group #{ group_id } ."
70
74
@logger . log ( Logger ::INFO , message )
71
- decide_reasons & .push ( message )
72
- return nil
75
+ decide_reasons . push ( message )
76
+ return nil , decide_reasons
73
77
end
74
78
75
79
# continue bucketing if the user is bucketed into the experiment specified
76
80
message = "User '#{ user_id } ' is in experiment '#{ experiment_key } ' of group #{ group_id } ."
77
81
@logger . log ( Logger ::INFO , message )
78
- decide_reasons & .push ( message )
82
+ decide_reasons . push ( message )
79
83
end
80
84
end
81
85
82
86
traffic_allocations = experiment [ 'trafficAllocation' ]
83
- variation_id = find_bucket ( bucketing_id , user_id , experiment_id , traffic_allocations , decide_reasons )
87
+ variation_id , find_bucket_reasons = find_bucket ( bucketing_id , user_id , experiment_id , traffic_allocations )
88
+ decide_reasons . push ( *find_bucket_reasons )
89
+
84
90
if variation_id && variation_id != ''
85
91
variation = project_config . get_variation_from_id ( experiment_key , variation_id )
86
- return variation
92
+ return variation , decide_reasons
87
93
end
88
94
89
95
# Handle the case when the traffic range is empty due to sticky bucketing
90
96
if variation_id == ''
91
97
message = 'Bucketed into an empty traffic range. Returning nil.'
92
98
@logger . log ( Logger ::DEBUG , message )
93
- decide_reasons & .push ( message )
99
+ decide_reasons . push ( message )
94
100
end
95
101
96
- nil
102
+ [ nil , decide_reasons ]
97
103
end
98
104
99
- def find_bucket ( bucketing_id , user_id , parent_id , traffic_allocations , decide_reasons = nil )
105
+ def find_bucket ( bucketing_id , user_id , parent_id , traffic_allocations )
100
106
# Helper function to find the matching entity ID for a given bucketing value in a list of traffic allocations.
101
107
#
102
108
# bucketing_id - String A customer-assigned value user to generate bucketing key
103
109
# user_id - String ID for user
104
110
# parent_id - String entity ID to use for bucketing ID
105
111
# traffic_allocations - Array of traffic allocations
106
112
#
107
- # Returns entity ID corresponding to the provided bucket value or nil if no match is found.
113
+ # Returns and array of two values where first value is the entity ID corresponding to the provided bucket value
114
+ # or nil if no match is found. The second value contains the array of reasons stating how the deicision was taken
115
+ decide_reasons = [ ]
108
116
bucketing_key = format ( BUCKETING_ID_TEMPLATE , bucketing_id : bucketing_id , entity_id : parent_id )
109
117
bucket_value = generate_bucket_value ( bucketing_key )
110
118
111
119
message = "Assigned bucket #{ bucket_value } to user '#{ user_id } ' with bucketing ID: '#{ bucketing_id } '."
112
120
@logger . log ( Logger ::DEBUG , message )
113
- decide_reasons &.push ( message )
114
121
115
122
traffic_allocations . each do |traffic_allocation |
116
123
current_end_of_range = traffic_allocation [ 'endOfRange' ]
117
124
if bucket_value < current_end_of_range
118
125
entity_id = traffic_allocation [ 'entityId' ]
119
- return entity_id
126
+ return entity_id , decide_reasons
120
127
end
121
128
end
122
129
123
- nil
130
+ [ nil , decide_reasons ]
124
131
end
125
132
126
133
private
0 commit comments