Skip to content

Conversation

@marcio-absmartly
Copy link
Contributor

@marcio-absmartly marcio-absmartly commented Dec 15, 2025

This PR add cache re-validation when attributes change after an assignment was cached.

Summary by CodeRabbit

  • Tests

    • Added comprehensive tests covering attribute-driven re-evaluation, audience matching transitions, strict/non-strict modes, exposure publishing interactions and caching behaviour.
  • New Features

    • Audience matching now re-evaluates when attributes change, updating assignments and cached results accordingly to reflect current attributes.
  • Chores

    • Package version bumped to 1.13.3.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 15, 2025

Walkthrough

This pull request adds per-attribute-sequence tracking to Context by introducing a private _attrsSeq counter and an optional attrsSeq?: number on Assignment. Attribute updates increment _attrsSeq, _getAttributesMap() centralises attribute extraction, and audience matching is re-evaluated only when _attrsSeq has advanced compared with an assignment's attrsSeq. Assignments are stamped with the current attrsSeq when established. Tests were extended to validate dynamic re-evaluation of audience expressions across strict/non-strict modes, peek/treatment paths, caching scenarios and exposure queuing interactions. package.json version was bumped to 1.13.3.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Review of src/context.ts changes: _attrsSeq lifecycle, propagation of attrsSeq into Assignment, and conditional audience re-evaluation in _assign.
  • Verification of _getAttributesMap() correctness and all replacements of previous inline attribute collection.
  • Tests in src/__tests__/context.test.js: coverage breadth and correctness of scenarios for strict vs non-strict, peek vs treatment, caching and exposure interactions.
  • Quick check of package.json version bump.

Poem

🐰 I hop through attrs with a counting tune,
A tiny seq keeps checks in tune,
When values change I take a glance,
Re-evaluate, then skip the dance,
Cache snug and logic bright — hop, hop, hooray! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: re-evaluating audience expressions when attributes are modified, which is the core functionality added throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/fix-assignment-cache-after-attr-change

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb6e25 and aca8aef.

📒 Files selected for processing (3)
  • package.json (1 hunks)
  • src/__tests__/context.test.js (2 hunks)
  • src/context.ts (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/__tests__/context.test.js (1)
src/context.ts (2)
  • Context (124-1003)
  • publisher (243-245)
🔇 Additional comments (10)
package.json (1)

3-3: LGTM! Version bump is appropriate.

The version increment from 1.13.2 to 1.13.3 is suitable for this patch release that adds cache re-validation when attributes change.

src/__tests__/context.test.js (2)

1313-1365: LGTM! Comprehensive test coverage for peek() re-evaluation.

These tests thoroughly verify the re-evaluation behaviour for peek() across three key scenarios:

  1. Strict mode with attribute changes affecting audience matching
  2. Non-strict mode maintaining assigned variant regardless of audience
  3. Proper cache usage when no new attributes are set

The tests correctly verify that peek() never queues exposures whilst still re-evaluating audience constraints when attributes change.


1800-2025: Excellent test coverage for treatment() re-evaluation scenarios!

These tests comprehensively verify the re-evaluation logic for treatment(), covering:

  1. Re-evaluation in both modes: Strict mode (variant changes) and non-strict mode (audienceMismatch flag changes)
  2. Exposure queuing: Correctly verifies new exposures are queued only when audience result changes
  3. Attribute timing: Distinguishes between attributes set before vs after assignment computation
  4. Cache optimization: Verifies that attrsSeq is updated even when audience result is unchanged, preventing repeated evaluation on subsequent calls
  5. Integration testing: Tests the full publish flow with detailed exposure data verification

The test at lines 1995-2025 is particularly valuable as it verifies the optimization that prevents repeated audience evaluation when the result hasn't changed.

src/context.ts (7)

65-65: LGTM! Backward-compatible type extension.

The addition of the optional attrsSeq field to the Assignment type is backward compatible and correctly tracks the attribute sequence at assignment time.


146-146: LGTM! Proper initialisation of sequence counter.

The _attrsSeq field is correctly initialised to 0 and will track attribute changes throughout the context lifecycle.

Also applies to: 169-169


325-330: LGTM! Correct sequence tracking on attribute changes.

The _attrsSeq counter is correctly incremented after each attribute is added, ensuring that audience constraints can be re-evaluated when attributes change.


443-449: LGTM! Clean helper method for attribute mapping.

This centralises the attribute map construction logic, ensuring consistency across audience evaluations. The method is only called when needed (during audience constraint evaluation), so the object creation overhead is acceptable.


462-476: LGTM! Solid re-evaluation logic with optimization.

The audienceMatches function correctly implements the cache invalidation logic:

  1. Conditional re-evaluation: Only evaluates when _attrsSeq has advanced (line 464)
  2. Change detection: Returns false when audience result has changed, triggering assignment recomputation (lines 468-470)
  3. Optimization: Updates assignment.attrsSeq even when result is unchanged (line 472), preventing repeated evaluation on subsequent calls
  4. Edge case handling: Properly handles undefined attrsSeq via nullish coalescing to 0

This logic ensures assignments are re-evaluated only when necessary whilst maintaining correctness.


495-495: LGTM! Correct integration of audience validation.

The addition of audienceMatches alongside experimentMatches ensures that cached assignments are only reused when both the experiment configuration and audience evaluation results remain unchanged.


532-532: LGTM! Consistent attribute handling and proper sequence stamping.

Line 532 correctly uses the centralised _getAttributesMap() method for consistency. Line 590 properly stamps the assignment with the current _attrsSeq, establishing the baseline for future re-evaluation checks.

Also applies to: 590-590

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.40.0)
src/__tests__/context.test.js

Comment @coderabbitai help to get the list of available commands and usage tips.

@marcio-absmartly marcio-absmartly force-pushed the bugfix/fix-assignment-cache-after-attr-change branch from 0cb6e25 to aca8aef Compare December 15, 2025 17:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/context.ts (1)

462-476: Consider adding a clarifying comment for the side effect.

The audienceMatches function mutates assignment.attrsSeq (line 472) even when returning true (cache valid). While this is intentional to prevent repeated re-evaluations when the audience result is unchanged, a brief inline comment would improve readability.

 				if (newAudienceMismatch !== assignment.audienceMismatch) {
 					return false;
 				}
 
+				// Update attrsSeq to avoid re-evaluating on subsequent calls when result unchanged
 				assignment.attrsSeq = this._attrsSeq;
 			}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9cca6d6 and 0cb6e25.

📒 Files selected for processing (2)
  • src/__tests__/context.test.js (2 hunks)
  • src/context.ts (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/__tests__/context.test.js (1)
src/context.ts (1)
  • publisher (243-245)
🔇 Additional comments (10)
src/__tests__/context.test.js (2)

1312-1365: Well-structured tests for peek() audience re-evaluation.

These tests effectively cover the key scenarios for audience re-evaluation via peek():

  1. Strict mode re-evaluation when attributes change
  2. Non-strict mode re-evaluation when attributes change
  3. Caching behaviour when no new attributes are set

The assertions correctly verify that pending() remains 0 since peek() should not queue exposures.


1799-2025: Comprehensive test coverage for treatment() audience re-evaluation.

The tests thoroughly cover:

  • Re-evaluation in strict and non-strict modes (lines 1800-1838)
  • Caching when no new attributes set (lines 1840-1857)
  • Experiments without audience filter (lines 1859-1876)
  • Transition from mismatch to match with exposure verification (lines 1878-1933)
  • Attribute timing scenarios (before vs after assignment) (lines 1935-1971)
  • Cache preservation when audience result unchanged (lines 1973-1993)
  • attrsSeq update to prevent repeated evaluations (lines 1995-2025)

The test at lines 1973-1993 is particularly valuable as it verifies that changing an attribute that doesn't affect the audience result (e.g., age 15 → 18, both still < 20) doesn't queue a new exposure.

src/context.ts (8)

65-66: LGTM - Optional attrsSeq field added to Assignment type.

The optional property correctly supports tracking the attribute sequence at the time of assignment.


146-146: LGTM - Private counter for tracking attribute mutations.

Correctly placed with other private state fields.


169-169: LGTM - Proper initialisation of _attrsSeq counter.

Initialising to 0 in the constructor is correct.


325-330: LGTM - Attribute sequence increment on attribute set.

Incrementing _attrsSeq after adding the attribute ensures the counter reflects when attributes have changed since the last evaluation.


443-449: LGTM - Helper method to build attributes map.

The implementation is consistent with the existing getAttribute() logic where the last value wins for duplicate attribute names.


494-498: LGTM - Proper integration of audience matching with experiment matching.

The short-circuit evaluation correctly checks experimentMatches first (cheaper metadata comparison) before audienceMatches (which may re-evaluate the audience expression).


531-537: LGTM - Consistent use of _getAttributesMap() helper.

Using the same helper for both initial audience evaluation and re-evaluation ensures consistent behaviour.


590-590: LGTM - Correct placement of attrsSeq assignment.

Setting attrsSeq at the end of the experiment assignment block ensures it captures the current attribute sequence when the assignment is created, enabling proper re-evaluation detection on subsequent calls.

@calthejuggler calthejuggler merged commit 87abcae into main Dec 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants