Skip to content

Commit 6c6e283

Browse files
prepare 5.6.1 release (#145)
1 parent bfa19c8 commit 6c6e283

File tree

2 files changed

+65
-5
lines changed

2 files changed

+65
-5
lines changed

lib/ldclient-rb/evaluation.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,20 +323,28 @@ def clause_match_user_no_segments(clause, user)
323323
end
324324

325325
def variation_index_for_user(flag, rule, user)
326-
if !rule[:variation].nil? # fixed variation
327-
return rule[:variation]
328-
elsif !rule[:rollout].nil? # percentage rollout
326+
variation = rule[:variation]
327+
return variation if !variation.nil? # fixed variation
328+
rollout = rule[:rollout]
329+
return nil if rollout.nil?
330+
variations = rollout[:variations]
331+
if !variations.nil? && variations.length > 0 # percentage rollout
329332
rollout = rule[:rollout]
330333
bucket_by = rollout[:bucketBy].nil? ? "key" : rollout[:bucketBy]
331334
bucket = bucket_user(user, flag[:key], bucket_by, flag[:salt])
332335
sum = 0;
333-
rollout[:variations].each do |variate|
336+
variations.each do |variate|
334337
sum += variate[:weight].to_f / 100000.0
335338
if bucket < sum
336339
return variate[:variation]
337340
end
338341
end
339-
nil
342+
# The user's bucket value was greater than or equal to the end of the last bucket. This could happen due
343+
# to a rounding error, or due to the fact that we are scaling to 100000 rather than 99999, or the flag
344+
# data could contain buckets that don't actually add up to 100000. Rather than returning an error in
345+
# this case (or changing the scaling, which would potentially change the results for *all* users), we
346+
# will simply put the user in the last bucket.
347+
variations[-1][:variation]
340348
else # the rule isn't well-formed
341349
nil
342350
end

spec/evaluation_spec.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,58 @@ def boolean_flag_with_clauses(clauses)
560560
end
561561
end
562562

563+
describe "variation_index_for_user" do
564+
it "matches bucket" do
565+
user = { key: "userkey" }
566+
flag_key = "flagkey"
567+
salt = "salt"
568+
569+
# First verify that with our test inputs, the bucket value will be greater than zero and less than 100000,
570+
# so we can construct a rollout whose second bucket just barely contains that value
571+
bucket_value = (bucket_user(user, flag_key, "key", salt) * 100000).truncate()
572+
expect(bucket_value).to be > 0
573+
expect(bucket_value).to be < 100000
574+
575+
bad_variation_a = 0
576+
matched_variation = 1
577+
bad_variation_b = 2
578+
rule = {
579+
rollout: {
580+
variations: [
581+
{ variation: bad_variation_a, weight: bucket_value }, # end of bucket range is not inclusive, so it will *not* match the target value
582+
{ variation: matched_variation, weight: 1 }, # size of this bucket is 1, so it only matches that specific value
583+
{ variation: bad_variation_b, weight: 100000 - (bucket_value + 1) }
584+
]
585+
}
586+
}
587+
flag = { key: flag_key, salt: salt }
588+
589+
result_variation = variation_index_for_user(flag, rule, user)
590+
expect(result_variation).to be matched_variation
591+
end
592+
593+
it "uses last bucket if bucket value is equal to total weight" do
594+
user = { key: "userkey" }
595+
flag_key = "flagkey"
596+
salt = "salt"
597+
598+
bucket_value = (bucket_user(user, flag_key, "key", salt) * 100000).truncate()
599+
600+
# We'll construct a list of variations that stops right at the target bucket value
601+
rule = {
602+
rollout: {
603+
variations: [
604+
{ variation: 0, weight: bucket_value }
605+
]
606+
}
607+
}
608+
flag = { key: flag_key, salt: salt }
609+
610+
result_variation = variation_index_for_user(flag, rule, user)
611+
expect(result_variation).to be 0
612+
end
613+
end
614+
563615
describe "bucket_user" do
564616
it "gets expected bucket values for specific keys" do
565617
user = { key: "userKeyA" }

0 commit comments

Comments
 (0)