@@ -20,7 +20,7 @@ module Optimizely
2020 class Bucketer
2121 # Optimizely bucketing algorithm that evenly distributes visitors.
2222
23- BUCKETING_ID_TEMPLATE = '%{user_id }%{entity_id}'
23+ BUCKETING_ID_TEMPLATE = '%{bucketing_id }%{entity_id}'
2424 HASH_SEED = 1
2525 MAX_HASH_VALUE = 2 **32
2626 MAX_TRAFFIC_VALUE = 10_000
@@ -35,13 +35,15 @@ def initialize(config)
3535 @config = config
3636 end
3737
38- def bucket ( experiment , user_id )
38+ def bucket ( experiment , bucketing_id , user_id )
3939 # Determines ID of variation to be shown for a given experiment key and user ID.
4040 #
4141 # experiment - Experiment for which visitor is to be bucketed.
42+ # bucketing_id - String A customer-assigned value used to generate the bucketing key
4243 # user_id - String ID for user.
4344 #
4445 # Returns variation in which visitor with ID user_id has been placed. Nil if no variation.
46+ return nil if experiment . nil?
4547
4648 # check if experiment is in a group; if so, check if user is bucketed into specified experiment
4749 experiment_id = experiment [ 'id' ]
@@ -51,7 +53,7 @@ def bucket(experiment, user_id)
5153 group = @config . group_key_map . fetch ( group_id )
5254 if Helpers ::Group . random_policy? ( group )
5355 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 )
5557 # return if the user is not bucketed into any experiment
5658 unless bucketed_experiment_id
5759 @config . logger . log ( Logger ::INFO , "User '#{ user_id } ' is in no experiment." )
@@ -76,7 +78,7 @@ def bucket(experiment, user_id)
7678 end
7779
7880 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 )
8082 if variation_id && variation_id != ''
8183 variation = @config . get_variation_from_id ( experiment_key , variation_id )
8284 variation_key = variation ? variation [ 'key' ] : nil
@@ -96,18 +98,18 @@ def bucket(experiment, user_id)
9698 nil
9799 end
98100
99- def find_bucket ( user_id , parent_id , traffic_allocations )
101+ def find_bucket ( bucketing_id , user_id , parent_id , traffic_allocations )
100102 # Helper function to find the matching entity ID for a given bucketing value in a list of traffic allocations.
101103 #
104+ # bucketing_id - String A customer-assigned value user to generate bucketing key
102105 # user_id - String ID for user
103106 # parent_id - String entity ID to use for bucketing ID
104107 # traffic_allocations - Array of traffic allocations
105108 #
106109 # 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 } '." )
111113
112114 traffic_allocations . each do |traffic_allocation |
113115 current_end_of_range = traffic_allocation [ 'endOfRange' ]
@@ -122,25 +124,25 @@ def find_bucket(user_id, parent_id, traffic_allocations)
122124
123125 private
124126
125- def generate_bucket_value ( bucketing_id )
127+ def generate_bucket_value ( bucketing_key )
126128 # Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE).
127129 #
128- # bucketing_id - String ID for bucketing.
130+ # bucketing_key - String - Value used to generate bucket value
129131 #
130- # Returns bucket value corresponding to the provided bucketing ID .
132+ # Returns bucket value corresponding to the provided bucketing key .
131133
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
133135 ( ratio * MAX_TRAFFIC_VALUE ) . to_i
134136 end
135137
136- def generate_unsigned_hash_code_32_bit ( bucketing_id )
138+ def generate_unsigned_hash_code_32_bit ( bucketing_key )
137139 # Helper function to retreive hash code
138140 #
139- # bucketing_id - String ID for bucketing.
141+ # bucketing_key - String - Value used for the key of the murmur hash
140142 #
141143 # Returns hash code which is a 32 bit unsigned integer.
142144
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
144146 end
145147 end
146148end
0 commit comments