@@ -20,7 +20,7 @@ module Optimizely
20
20
class Bucketer
21
21
# Optimizely bucketing algorithm that evenly distributes visitors.
22
22
23
- BUCKETING_ID_TEMPLATE = '%{user_id }%{entity_id}'
23
+ BUCKETING_ID_TEMPLATE = '%{bucketing_id }%{entity_id}'
24
24
HASH_SEED = 1
25
25
MAX_HASH_VALUE = 2 **32
26
26
MAX_TRAFFIC_VALUE = 10_000
@@ -35,13 +35,15 @@ def initialize(config)
35
35
@config = config
36
36
end
37
37
38
- def bucket ( experiment , user_id )
38
+ def bucket ( 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
# experiment - Experiment for which visitor is to be bucketed.
42
+ # bucketing_id - String A customer-assigned value used to generate the bucketing key
42
43
# user_id - String ID for user.
43
44
#
44
45
# Returns variation in which visitor with ID user_id has been placed. Nil if no variation.
46
+ return nil if experiment . nil?
45
47
46
48
# check if experiment is in a group; if so, check if user is bucketed into specified experiment
47
49
experiment_id = experiment [ 'id' ]
@@ -51,7 +53,7 @@ def bucket(experiment, user_id)
51
53
group = @config . group_key_map . fetch ( group_id )
52
54
if Helpers ::Group . random_policy? ( group )
53
55
traffic_allocations = group . fetch ( 'trafficAllocation' )
54
- bucketed_experiment_id = find_bucket ( user_id , group_id , traffic_allocations )
56
+ bucketed_experiment_id = find_bucket ( bucketing_id , user_id , group_id , traffic_allocations )
55
57
# return if the user is not bucketed into any experiment
56
58
unless bucketed_experiment_id
57
59
@config . logger . log ( Logger ::INFO , "User '#{ user_id } ' is in no experiment." )
@@ -76,7 +78,7 @@ def bucket(experiment, user_id)
76
78
end
77
79
78
80
traffic_allocations = experiment [ 'trafficAllocation' ]
79
- variation_id = find_bucket ( user_id , experiment_id , traffic_allocations )
81
+ variation_id = find_bucket ( bucketing_id , user_id , experiment_id , traffic_allocations )
80
82
if variation_id && variation_id != ''
81
83
variation = @config . get_variation_from_id ( experiment_key , variation_id )
82
84
variation_key = variation ? variation [ 'key' ] : nil
@@ -96,18 +98,18 @@ def bucket(experiment, user_id)
96
98
nil
97
99
end
98
100
99
- def find_bucket ( user_id , parent_id , traffic_allocations )
101
+ def find_bucket ( bucketing_id , user_id , parent_id , traffic_allocations )
100
102
# Helper function to find the matching entity ID for a given bucketing value in a list of traffic allocations.
101
103
#
104
+ # bucketing_id - String A customer-assigned value user to generate bucketing key
102
105
# user_id - String ID for user
103
106
# parent_id - String entity ID to use for bucketing ID
104
107
# traffic_allocations - Array of traffic allocations
105
108
#
106
109
# Returns entity ID corresponding to the provided bucket value or nil if no match is found.
107
-
108
- bucketing_id = sprintf ( BUCKETING_ID_TEMPLATE , user_id : user_id , entity_id : parent_id )
109
- bucket_value = generate_bucket_value ( bucketing_id )
110
- @config . logger . log ( Logger ::DEBUG , "Assigned bucket #{ bucket_value } to user '#{ user_id } '." )
110
+ bucketing_key = sprintf ( BUCKETING_ID_TEMPLATE , bucketing_id : bucketing_id , entity_id : parent_id )
111
+ bucket_value = generate_bucket_value ( bucketing_key )
112
+ @config . logger . log ( Logger ::DEBUG , "Assigned bucket #{ bucket_value } to user '#{ user_id } ' with bucketing ID: '#{ bucketing_id } '." )
111
113
112
114
traffic_allocations . each do |traffic_allocation |
113
115
current_end_of_range = traffic_allocation [ 'endOfRange' ]
@@ -122,25 +124,25 @@ def find_bucket(user_id, parent_id, traffic_allocations)
122
124
123
125
private
124
126
125
- def generate_bucket_value ( bucketing_id )
127
+ def generate_bucket_value ( bucketing_key )
126
128
# Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE).
127
129
#
128
- # bucketing_id - String ID for bucketing.
130
+ # bucketing_key - String - Value used to generate bucket value
129
131
#
130
- # Returns bucket value corresponding to the provided bucketing ID .
132
+ # Returns bucket value corresponding to the provided bucketing key .
131
133
132
- ratio = ( generate_unsigned_hash_code_32_bit ( bucketing_id ) ) . to_f / MAX_HASH_VALUE
134
+ ratio = ( generate_unsigned_hash_code_32_bit ( bucketing_key ) ) . to_f / MAX_HASH_VALUE
133
135
( ratio * MAX_TRAFFIC_VALUE ) . to_i
134
136
end
135
137
136
- def generate_unsigned_hash_code_32_bit ( bucketing_id )
138
+ def generate_unsigned_hash_code_32_bit ( bucketing_key )
137
139
# Helper function to retreive hash code
138
140
#
139
- # bucketing_id - String ID for bucketing.
141
+ # bucketing_key - String - Value used for the key of the murmur hash
140
142
#
141
143
# Returns hash code which is a 32 bit unsigned integer.
142
144
143
- MurmurHash3 ::V32 . str_hash ( bucketing_id , @bucket_seed ) & UNSIGNED_MAX_32_BIT_VALUE
145
+ MurmurHash3 ::V32 . str_hash ( bucketing_key , @bucket_seed ) & UNSIGNED_MAX_32_BIT_VALUE
144
146
end
145
147
end
146
148
end
0 commit comments