Skip to content

Clone cache to prevent mutation of cache#3122

Open
YishaiGlasner wants to merge 2 commits intomasterfrom
bug/sc-41337/-max-1hr-indefinite-loading-on-one-translation
Open

Clone cache to prevent mutation of cache#3122
YishaiGlasner wants to merge 2 commits intomasterfrom
bug/sc-41337/-max-1hr-indefinite-loading-on-one-translation

Conversation

@YishaiGlasner
Copy link
Contributor

@YishaiGlasner YishaiGlasner commented Mar 2, 2026

Clone before returning from cache. When unpacking from props clone before saving in cache.
That solves bug of not-loading translations.

Important note

I'm open to the claim that it costs too much in performance, so the fix should be finding where the mutation happens and fix there. But, as we're using the cache, it's just expected that it will occur more that something is taken from cache, passed thru some component and then someone changes it without knowing he corrupts the cache.

…. that solves bug of not-loading translations.
Copy link
Contributor

Copilot AI left a comment

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 aims to prevent cached API/text objects from being mutated after caching, addressing a bug where translations were not loading due to unintended shared-reference mutations.

Changes:

  • Clone values before storing them in the APIV3 texts cache (_textsStoreSet).
  • Clone values before storing them in the generic _cachedApiPromise cache.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 608 to 610
_textsStoreSet: function(key, value) {
this._textsStore[key] = value;
this._textsStore[key] = Sefaria.util.clone(value);
},
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

Cloning on set prevents later mutations of the original value from affecting the cache, but callers that read from _textsStore still receive the cached object by reference (e.g., via _cachedApiPromise), so they can still mutate and corrupt the cache after a cache hit. If the goal is to make the cache mutation-safe, consider returning a cloned copy on reads (or freezing cached objects in dev) rather than only cloning on writes.

Copilot uses AI. Check for mistakes.
Comment on lines 3635 to 3642
store[key] = data;
store[key] = Sefaria.util.clone(data);
return data;
})
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

_cachedApiPromise now caches a clone but returns the original data on a cache miss, while returning the cached value (by reference) on a cache hit. This makes the first result a different object than subsequent results and still allows cache corruption when callers mutate the returned object after a cache hit. Consider returning the cached cloned object consistently (and/or cloning on reads if you need strong immutability guarantees).

Copilot uses AI. Check for mistakes.
Comment on lines 3638 to 3641
.then(data => {
if (processor) { data = processor(data); }
store[key] = data;
store[key] = Sefaria.util.clone(data);
return data;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

Deep-cloning every _cachedApiPromise response can be expensive for large payloads (e.g., bulk text / text API responses) and doubles memory for cached entries. If this is intended mainly for specific caches (like _textsStore), consider limiting cloning to those stores or gating it behind a debug/opt-in flag to avoid a broad performance regression.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@mergify
Copy link

mergify bot commented Mar 2, 2026

🧪 CI Insights

Here's what we observed from your CI run for a92fc41.

🟢 All jobs passed!

But CI Insights is watching 👀

@YishaiGlasner YishaiGlasner changed the title Cclone before saving in cache to prevent mutation of cache Clone cache to prevent mutation of cache Mar 5, 2026
Copy link
Contributor

@stevekaplan123 stevekaplan123 left a comment

Choose a reason for hiding this comment

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

I'm worried about the expense. I think we should check if this affects performance for certain API calls first.

@yitzhakc
Copy link
Contributor

yitzhakc commented Mar 8, 2026 via email

@YishaiGlasner
Copy link
Contributor Author

props clone before saving in cache. That solves bug of not-loading translations.

Important note

I'm open to the claim that it costs too much in performance

@yitzhakc it's a section

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.

4 participants