Module: LaunchDarkly::Impl::EvaluatorBucketing

Defined in:
lib/ldclient-rb/impl/evaluator_bucketing.rb

Overview

Encapsulates the logic for percentage rollouts.

Since:

  • 5.5.0

Class Method Summary collapse

Class Method Details

.bucket_user(user, key, bucket_by, salt) ⇒ Number

Returns a user’s bucket value as a floating-point value in ‘[0, 1)`.

Parameters:

  • user (Object)

    the user properties

  • key (String)

    the feature flag key (or segment key, if this is for a segment rule)

  • bucket_by (String|Symbol)

    the name of the user attribute to be used for bucketing

  • salt (String)

    the feature flag’s or segment’s salt value

Returns:

  • (Number)

    the bucket value, from 0 inclusive to 1 exclusive

Since:

  • 5.5.0



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ldclient-rb/impl/evaluator_bucketing.rb', line 47

def self.bucket_user(user, key, bucket_by, salt)
  return nil unless user[:key]

  id_hash = bucketable_string_value(EvaluatorOperators.user_value(user, bucket_by))
  if id_hash.nil?
    return 0.0
  end

  if user[:secondary]
    id_hash += "." + user[:secondary].to_s
  end

  hash_key = "%s.%s.%s" % [key, salt, id_hash]

  hash_val = (Digest::SHA1.hexdigest(hash_key))[0..14]
  hash_val.to_i(16) / Float(0xFFFFFFFFFFFFFFF)
end

.variation_index_for_user(flag, rule, user) ⇒ Number

Applies either a fixed variation or a rollout for a rule (or the fallthrough rule).

Parameters:

  • flag (Object)

    the feature flag

  • rule (Object)

    the rule

  • user (Object)

    the user properties

Returns:

  • (Number)

    the variation index, or nil if there is an error

Since:

  • 5.5.0



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ldclient-rb/impl/evaluator_bucketing.rb', line 12

def self.variation_index_for_user(flag, rule, user)
  variation = rule[:variation]
  return variation if !variation.nil? # fixed variation
  rollout = rule[:rollout]
  return nil if rollout.nil?
  variations = rollout[:variations]
  if !variations.nil? && variations.length > 0 # percentage rollout
    rollout = rule[:rollout]
    bucket_by = rollout[:bucketBy].nil? ? "key" : rollout[:bucketBy]
    bucket = bucket_user(user, flag[:key], bucket_by, flag[:salt])
    sum = 0;
    variations.each do |variate|
      sum += variate[:weight].to_f / 100000.0
      if bucket < sum
        return variate[:variation]
      end
    end
    # The user's bucket value was greater than or equal to the end of the last bucket. This could happen due
    # to a rounding error, or due to the fact that we are scaling to 100000 rather than 99999, or the flag
    # data could contain buckets that don't actually add up to 100000. Rather than returning an error in
    # this case (or changing the scaling, which would potentially change the results for *all* users), we
    # will simply put the user in the last bucket.
    variations[-1][:variation]
  else # the rule isn't well-formed
    nil
  end
end