flagd: fractional operator uses wrong bucketing formula — unsigned u32::MAX divisor instead of signed i32::MAX

Open Beginner friendly
#102 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
75/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
rust
Domain
backend

Research direction

Start in the Rust in-process provider at fractional.rs lines 78-79 and inspect the existing fractional-operator test coverage. Verify the bucket calculation against the signed i32::MAX-based formula described in the issue, then run the relevant test suite; done means the Rust provider produces cross-SDK-consistent assignments.

Written by the indexing model from the issue text.

Description

bug

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

  • open-feature/flagd#1872 — Cross-SDK fractional operator bucketing inconsistencies
  • #84 — Harden hashing consistency (ADR for future CBOR-based approach, separate from this bug)
Dominant language
Rust
Stars
15
Forks
18
Avg merge
2d 1h
Merged PRs (30d)
1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from open-feature/rust-sdk-contrib

All issues in open-feature/rust-sdk-contrib

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.