Remove per-request JSON deep copy from getCachedValidExperiments - #3277
Open
danoswaltCL wants to merge 3 commits into
Open
Remove per-request JSON deep copy from getCachedValidExperiments#3277danoswaltCL wants to merge 3 commits into
danoswaltCL wants to merge 3 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes the per-request JSON.parse(JSON.stringify(...)) deep copy from ExperimentService.getCachedValidExperiments() by updating formattingConditionPayload() to avoid mutating cached experiment objects, and adds unit tests to guard the non-mutation invariant.
Changes:
- Removed the defensive JSON deep copy on cache hits in
getCachedValidExperiments(). - Reworked
formattingConditionPayload()to rebuild stripped conditions/decision points as new objects rather than deleting fields in place. - Added unit tests verifying
formattingConditionPayload()does not mutate its input and maintains expected reference/back-reference behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/backend/src/api/services/ExperimentService.ts | Removes cache-hit deep copy and updates formattingConditionPayload() to avoid mutating cached graphs. |
| packages/backend/src/api/services/ExperimentAssignmentService.ts | Updates inline documentation about cached experiment mutation expectations. |
| packages/backend/test/unit/services/ExperimentService.test.ts | Adds non-mutation and stability tests for formattingConditionPayload(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
getCachedValidExperiments wrapped every cache hit in JSON.parse(JSON.stringify(...)). That defensive copy existed because formattingConditionPayload mutated the experiment it was given — it deleted conditionPayloads off the cached conditions/partitions in place. Since the cache is in-memory and hands back the same object reference to every request, mutating there would corrupt the cached graph for all subsequent requests. formattingConditionPayload now rebuilds the stripped conditions and partitions as new objects instead of deleting fields in place, so the cached graph is never written to and the per-request deep copy is gone. The parentCondition/decisionPoint back-references point at the rebuilt objects, so reference identity within the returned experiment matches what the in-place version produced. Five tests in ExperimentService.test.ts guard the non-mutation invariant that makes removing the copy safe, covering both the simple and factorial paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e(JSON.stringify(exp))
danoswaltCL
force-pushed
the
bugfix/remove-json-deep-copy-from-get-cached-experiments
branch
from
August 10, 2026 17:37
af3d2b3 to
03e4de7
Compare
danoswaltCL
marked this pull request as ready for review
August 10, 2026 17:38
bcb37
approved these changes
Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I'm leaving this one in draft for now because i need to test it more, but copilot can give it a crack to validate claude's claims:
per Claude:
getCachedValidExperiments wrapped every cache hit in JSON.parse(JSON.stringify(...)). That defensive copy existed because formattingConditionPayload mutated the experiment it was given — it deleted conditionPayloads off the cached conditions/partitions in place. Since the cache is in-memory and hands back the same object reference to every request, mutating there would corrupt the cached graph for all subsequent requests.
formattingConditionPayload now rebuilds the stripped conditions and partitions as new objects instead of deleting fields in place, so the cached graph is never written to and the per-request deep copy is gone. The parentCondition/decisionPoint back-references point at the rebuilt objects, so reference identity within the returned experiment matches what the in-place version produced.
Five tests in ExperimentService.test.ts guard the non-mutation invariant that makes removing the copy safe, covering both the simple and factorial paths.