Skip to content
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
merged 2 commits into from
Nov 3, 2020

Conversation

benjamn
Copy link
Member

@benjamn benjamn commented Nov 2, 2020

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 means cache.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.

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.
@manojVivek
Copy link

@benjamn Thanks for the quick turnaround here!

Any chance we can get this fix for the v3.2.* tree?

Copy link
Member

@hwillson hwillson left a 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 benjamn merged commit 49e68a3 into release-3.3 Nov 3, 2020
@benjamn benjamn deleted the reactiveVar.forgetCache branch November 3, 2020 18:35
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.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants