-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent reactive variables from retaining unwatched caches. #7279
Merged
Conversation
This file contains 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
Each reactive variable must remember any caches previously involved in reading its value, so those caches can be notified if/when the variable value is next modified. To prevent reactive variables from retaining references to caches that no longer need to be notified, we preemptively remove the cache from the memory of all of its associated reactive variables whenever all of its watches are cancelled, since not having any active cache.watches means cache.broadcastWatches has nothing to do. Should prevent memory leaks when many caches are created and discarded by the same process, as in the SSR use case described in #7274.
@benjamn Thanks for the quick turnaround here! Any chance we can get this fix for the v3.2.* tree? |
hwillson
approved these changes
Nov 3, 2020
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great @benjamn - thanks!
benjamn
added a commit
that referenced
this pull request
Nov 20, 2020
In the context of a larger cache read operation, accessing a reactive variable automatically associates the variable with the cache, so future updates to the variable can be broadcast to the cache. Outside this context, it may not be possible for the variable to discover the currently active cache. For example, there is no currently active cache in asynchronous ApolloLink code (#7338). Since we recently added a `reactiveVar.forgetCache(cache)` in #7279, I think it makes sense to have a manual way to do the opposite, which I have decided to call `reactiveVar.attachCache(cache)`. Although this attachment is manual at the moment, in a hypothetical future where the client/cache manage the persistence of reactive variables, it should be easy to attach the appropriate cache to those variables when they are first initialized, which would enable reactive updates from literally anywhere, with no additional effort by the developer.
benjamn
added a commit
that referenced
this pull request
Nov 20, 2020
In the context of a larger cache read operation, accessing a reactive variable automatically associates the variable with the cache, so future updates to the variable can be broadcast to the cache. Outside this context, it may not be possible for the variable to discover the currently active cache. For example, there is no currently active cache in asynchronous ApolloLink code (#7338). Since we recently added a `reactiveVar.forgetCache(cache)` in #7279, I think it makes sense to have a manual way to do the opposite, which I have decided to call `reactiveVar.attachCache(cache)`. Although this attachment is manual at the moment, in a hypothetical future where the client/cache manage the persistence of reactive variables, it should be easy to attach the appropriate cache to those variables when they are first initialized, which would enable reactive updates from literally anywhere, with no additional effort by the developer.
benjamn
added a commit
that referenced
this pull request
Feb 4, 2021
Fixes a bug introduced by #7279, which was triggered by calling this.cancel() before re-watching in the QueryInfo#updateWatch method, causing cache.watches.size to drop to zero temporarily, thereby invoking forgetCache(cache). When this happened, reactive variables would stop broadcasting updates to the forgotten cache, even though the unwatching was only momentary, and would not begin broadcasting again until the association was reestablished by other means. This new implementation uses a WeakMap to remember the association between caches and sets of reactive variables, which makes it possible to recall those associations later, provided the cache has not been garbage collected in the meantime.
benjamn
added a commit
that referenced
this pull request
Feb 4, 2021
Fixes a bug introduced by #7279, which was triggered by calling this.cancel() before re-watching in the QueryInfo#updateWatch method, causing cache.watches.size to drop to zero temporarily, thereby invoking forgetCache(cache). When this happened, reactive variables would stop broadcasting updates to the forgotten cache, even though the unwatching was only momentary, and would not begin broadcasting again until the association was reestablished by other means. This new implementation uses a WeakMap to remember the association between caches and sets of reactive variables, which makes it possible to recall those associations later, provided the cache has not been garbage collected in the meantime.
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Each reactive variable must remember any caches previously involved in reading its value, so those caches can be notified if/when the variable value is next modified.
To prevent reactive variables from retaining references to caches that no longer need to be notified, we can preemptively remove the cache from the memory of all of its associated reactive variables whenever all of its watches have been cancelled, since not having any active
cache.watches
meanscache.broadcastWatches
has nothing to do.This change should prevent memory leaks when lots of caches are created and discarded by the same process, as in the server-side rendering use case described by @manojVivek in #7274.