Summary
The fractional operator's MurmurHash bucketing formula in the Rust in-process provider uses an unsigned division that produces different bucket assignments from every other SDK. Users running the same flag configuration across multiple SDKs will get inconsistent variant assignments for the same targeting key.
Current Behavior
fractional.rs:78-79:
let hash: u32 = murmurhash3_x86_32(bucket_by.as_bytes(), 0);
let bucket = (hash as f64 / u32::MAX as f64) * 100.0;
This divides the raw unsigned 32-bit hash by u32::MAX (4,294,967,295), producing a bucket in [0, 100].
Expected Behavior
All other SDKs cast to signed i32, take the absolute value, and divide by i32::MAX (2,147,483,647):
Go:
hashValue := int32(murmur3.StringSum32(value))
hashRatio := math.Abs(float64(hashValue)) / math.MaxInt32
bucket := hashRatio * 100
Java:
int mmrHash = MurmurHash3.hash32x86(bytes, 0, bytes.length, 0);
float bucket = Math.abs(mmrHash) * 1.0f / Integer.MAX_VALUE * 100;
Python:
hash_ratio = abs(mmh3.hash(bucket_by)) / (2**31 - 1)
bucket = hash_ratio * 100
The Rust implementation should be:
let hash: u32 = murmurhash3_x86_32(bucket_by.as_bytes(), 0);
let signed = hash as i32;
let bucket = (signed as f64).abs() / (i32::MAX as f64) * 100.0;
Impact
For any given (flagKey, targetingKey) pair, the Rust provider will assign a different bucket value than Go/Java/JS/Python/.NET. This means users migrating between SDKs or running multiple SDKs against the same flag configuration will see different variant assignments.
Related
Summary
The
fractionaloperator's MurmurHash bucketing formula in the Rust in-process provider uses an unsigned division that produces different bucket assignments from every other SDK. Users running the same flag configuration across multiple SDKs will get inconsistent variant assignments for the same targeting key.Current Behavior
fractional.rs:78-79:This divides the raw unsigned 32-bit hash by
u32::MAX(4,294,967,295), producing a bucket in[0, 100].Expected Behavior
All other SDKs cast to signed
i32, take the absolute value, and divide byi32::MAX(2,147,483,647):Go:
Java:
Python:
The Rust implementation should be:
Impact
For any given
(flagKey, targetingKey)pair, the Rust provider will assign a different bucket value than Go/Java/JS/Python/.NET. This means users migrating between SDKs or running multiple SDKs against the same flag configuration will see different variant assignments.Related
fractionaloperator bucketing inconsistencies