spotify / spotify/confidence-resolver
Rust resolver returns NO_SEGMENT_MATCH when materialization has no variant for rule
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 16
- Forks
- 14
- Avg merge
- 2d 7m
- Merged PRs (30d)
- 40
Description
Problem
When a materialization record exists for the unit but has no variant for the matched rule (empty ruleToVariant), the Rust resolver sets materialization_matched = Some(false) and falls back to segment matching. If the segment targeting doesn't match (e.g. wrong country), the result is NO_SEGMENT_MATCH. Java falls through to hash-based bucket assignment and returns MATCH.
Rust behavior
confidence-resolver/src/lib.rs ~L1072-1128:
match materialization_context.has_rule_materialization(
read_materialization, &unit, &rule.name,
)? {
Some(false) => {
materialization_matched = Some(false); // <-- blocks fallthrough
if read_mode.materialization_must_match {
continue;
}
}
Some(true) => {
// ... try to select assignment from materialization
}
None => { ... }
}
if materialization_matched != Some(true) {
// falls back to segment_match, which may fail
materialization_matched = match self.segment_match(...) { ... };
}
When has_rule_materialization returns Some(false) (unit is in materialization but no variant for this rule), Rust sets materialization_matched = Some(false). This forces segment matching. If the segment targeting fails (e.g. country mismatch), the rule is skipped → NO_SEGMENT_MATCH.
Java behavior
AccountResolver.java ~L287-369:
} else {
// Unit IS in materialization
// ... targeting check ...
if (materializationMatched) {
final Optional<String> variant = materializationInfo.getVariantForRule(rule.getName());
if (variant.isPresent()) {
// Use materialization variant
return variantMatch(...);
}
// variant is empty → falls through
}
}
// Falls through to segment matching + bucket assignment
if (!materializationMatched && !segmentMatches(...)) {
continue;
}
// Hash-based bucket assignment
final long bucket = Randomizer.getBucket(unit.get(), variantSalt, bucketCount);
matchedAssignmentOpt = spec.getAssignmentsList().stream()
.filter(variant -> Randomizer.coversBucket(variant, bucket))
.findFirst();
When getVariantForRule() returns empty, Java does not set materializationMatched = false. It falls through to the hash-based bucket assignment, which uses the targeting key to compute a bucket and pick a variant. The !materializationMatched check passes because materializationMatched was set to true during the targeting check above, so segment matching is skipped entirely.
Affected spec tests
mat_matched_no_variant_for_rule
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in confidence-resolver/src/lib.rs around lines 1072-1128 and compare the Rust flow with AccountResolver.java around lines 287-369. Run the affected spec test mat_matched_no_variant_for_rule and inspect the materialization and bucket-assignment tests. Done means the empty rule variant follows the documented Java behavior and the affected test passes without regressing segment matching.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, rust
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100