Skip to content

Remove per-request JSON deep copy from getCachedValidExperiments - #3277

Open
danoswaltCL wants to merge 3 commits into
release/6.6from
bugfix/remove-json-deep-copy-from-get-cached-experiments
Open

Remove per-request JSON deep copy from getCachedValidExperiments#3277
danoswaltCL wants to merge 3 commits into
release/6.6from
bugfix/remove-json-deep-copy-from-get-cached-experiments

Conversation

@danoswaltCL

Copy link
Copy Markdown
Collaborator

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.

@danoswaltCL
danoswaltCL requested review from bcb37 and a lite review from Copilot August 7, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Comment thread packages/backend/src/api/services/ExperimentService.ts
Comment thread packages/backend/src/api/services/ExperimentAssignmentService.ts
danoswaltCL and others added 2 commits August 10, 2026 13:32
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>
@danoswaltCL
danoswaltCL force-pushed the bugfix/remove-json-deep-copy-from-get-cached-experiments branch from af3d2b3 to 03e4de7 Compare August 10, 2026 17:37
@danoswaltCL
danoswaltCL marked this pull request as ready for review August 10, 2026 17:38
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