Skip to content

Commit db8dca4

Browse files
fix: use 32bit murmur calculation (64 is not stable) (#913)
* fixes murmur3 hash calculation to be cross language compatible (32bit). Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
1 parent 2eda6ab commit db8dca4

File tree

4 files changed

+20
-25
lines changed

4 files changed

+20
-25
lines changed

core/pkg/eval/fractional_evaluation.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,11 @@ func parseFractionalEvaluationDistributions(values []any) ([]fractionalEvaluatio
108108
return feDistributions, nil
109109
}
110110

111+
// distributeValue calculate hash for given hash key and find the bucket distributions belongs to
111112
func distributeValue(value string, feDistribution []fractionalEvaluationDistribution) string {
112-
hashValue := murmur3.StringSum64(value)
113-
114-
hashRatio := float64(hashValue) / math.MaxUint64
115-
116-
bucket := int(hashRatio * 100) // integer in range [0, 99]
113+
hashValue := int32(murmur3.StringSum32(value))
114+
hashRatio := math.Abs(float64(hashValue)) / math.MaxInt32
115+
bucket := int(hashRatio * 100) // in range [0, 100]
117116

118117
rangeEnd := 0
119118
for _, dist := range feDistribution {

core/pkg/eval/fractional_evaluation_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ func TestFractionalEvaluation(t *testing.T) {
7979
context: map[string]any{
8080
"email": "monica@faas.com",
8181
},
82-
expectedVariant: "green",
83-
expectedValue: "#00FF00",
82+
expectedVariant: "blue",
83+
expectedValue: "#0000FF",
8484
expectedReason: model.TargetingMatchReason,
8585
},
8686
"joey@faas.com": {
@@ -89,8 +89,8 @@ func TestFractionalEvaluation(t *testing.T) {
8989
context: map[string]any{
9090
"email": "joey@faas.com",
9191
},
92-
expectedVariant: "blue",
93-
expectedValue: "#0000FF",
92+
expectedVariant: "red",
93+
expectedValue: "#FF0000",
9494
expectedReason: model.TargetingMatchReason,
9595
},
9696
"ross@faas.com": {
@@ -99,8 +99,8 @@ func TestFractionalEvaluation(t *testing.T) {
9999
context: map[string]any{
100100
"email": "ross@faas.com",
101101
},
102-
expectedVariant: "red",
103-
expectedValue: "#FF0000",
102+
expectedVariant: "green",
103+
expectedValue: "#00FF00",
104104
expectedReason: model.TargetingMatchReason,
105105
},
106106
"ross@faas.com with different flag key": {
@@ -152,8 +152,8 @@ func TestFractionalEvaluation(t *testing.T) {
152152
context: map[string]any{
153153
"email": "ross@faas.com",
154154
},
155-
expectedVariant: "blue",
156-
expectedValue: "#0000FF",
155+
expectedVariant: "red",
156+
expectedValue: "#FF0000",
157157
expectedReason: model.TargetingMatchReason,
158158
},
159159
"non even split": {
@@ -201,8 +201,8 @@ func TestFractionalEvaluation(t *testing.T) {
201201
context: map[string]any{
202202
"email": "test4@faas.com",
203203
},
204-
expectedVariant: "green",
205-
expectedValue: "#00FF00",
204+
expectedVariant: "red",
205+
expectedValue: "#FF0000",
206206
expectedReason: model.TargetingMatchReason,
207207
},
208208
"fallback to default variant if no email provided": {
@@ -346,8 +346,8 @@ func TestFractionalEvaluation(t *testing.T) {
346346
context: map[string]any{
347347
"targetingKey": "foo@foo.com",
348348
},
349-
expectedVariant: "green",
350-
expectedValue: "#00FF00",
349+
expectedVariant: "blue",
350+
expectedValue: "#0000FF",
351351
expectedReason: model.TargetingMatchReason,
352352
},
353353
}

docs/other_resources/in-process-providers/evaluators/fractional_evaluation.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,9 @@ func FractionalEvaluation(values, data interface{}) interface{} {
168168
return nil
169169
}
170170

171-
// 4. Calculate the hash of the target property and map it to a number between [0, 99]
172-
hashValue := murmur3.HashString(value)
173-
174-
// divide the hash value by the largest possible value, integer 2^64
175-
hashRatio := float64(hashValue) / math.Pow(2, 64)
176-
177-
// integer in range [0, 99]
171+
// 4. Calculate the hash of the target property and map it to a number between [0, 99]
172+
hashValue := int32(murmur3.StringSum32(value))
173+
hashRatio := math.Abs(float64(hashValue)) / math.MaxInt32
178174
bucket := int(hashRatio * 100)
179175

180176
// 5. Iterate through the variant and increment the threshold by the percentage of each variant.

test-harness

0 commit comments

Comments
 (0)