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

Fix map memory leak #3444

Merged
merged 9 commits into from
May 23, 2018
Merged

Fix map memory leak #3444

merged 9 commits into from
May 23, 2018

Conversation

brunorzn
Copy link

@brunorzn brunorzn commented May 11, 2018

using object as keys in map can cause memory leaks since if the reference to the same-value object is different, it will not find it.

This is the case, for example, when you use InMemoryCache during SSR.

Example of code that reproduces the leak :


function loop() {
  console.log('Will loop in a few seconds...');
  return new Promise((resolve) => {
    setTimeout(() => {
      for (let i = 0; i < 10000; i += 1) {
        console.log('writing some data in memory cache...');
        const cache = new InMemoryCache();
        cache.writeData({ data: { Test: { value: 7, __typename: 'Test' } } });
        console.log('done.');
      }
      resolve();
    }, 3000);
  });
}

async function run() {
  while (true) {
    await loop();
  }
}

run();

also, see issue apollographql/apollo-link-state#248

Checklist:

  • If this PR is a new feature, please reference an issue where a consensus about the design was reached (not necessary for small changes)
  • Make sure all of the significant new logic is covered by tests
  • If this was a change that affects the external API used in GitHunt-React, update GitHunt-React and post a link to the PR in the discussion.

@apollo-cla
Copy link

@brunorzn: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Meteor Contributor Agreement here: https://contribute.meteor.com/

abernix added a commit that referenced this pull request May 23, 2018
…defined.

As demonstrated in the Danger test[0] on #3444,
the `author` property is not always set.  While it could make sense to
individually look at each individual commit's "login", Git commits are not
always tied to GitHub users so it seems more relevant to look at the GitHub
login of the user (or bot!) opening the PR, when making decisions about
whether to apply bot-only policies.

[0]: https://circleci.com/gh/apollographql/apollo-client/8427
abernix added a commit that referenced this pull request May 23, 2018
…ub user. (#3502)

From my commit message on 5d3889c:

---

As demonstrated in the Danger test[0] on #3444, the `author` property is not always set.  While it could make sense to individually look at each individual commit's "login", Git commits are not always tied to GitHub users so it seems more relevant to look at the GitHub login of the user (or bot!) opening the PR, when making decisions about whether to apply bot-only policies.

[0]: https://circleci.com/gh/apollographql/apollo-client/8427
@hwillson
Copy link
Member

Hi @brunorzn - it looks like the tests need to be adjusted a bit, to accommodate for the cache removal. If you don't mind adjusting the tests, I'll do a final review when everything is passing. Thanks!

@brunorzn
Copy link
Author

@hwillson : sorry about those failing tests. I did it too quickly during a meeting. I just felt like removing this couple of "should memoize and return cached results" tests.

Also, strangely, I had to modify the end of this test file, by adding an extra }), see commit 4557ca7 : what do you think ?

The tests should now pass.

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.

Awesome @brunorzn - this all looks great! Thanks very much! I've updated the CHANGELOG, and will merge once the tests have completed. Thanks again!

@@ -982,4 +952,5 @@ describe('getDirectivesFromDocument', () => {
const doc = getDirectivesFromDocument([{ name: 'client' }], query, true);
expect(print(doc)).toBe(print(expected));
});
});
Copy link
Member

Choose a reason for hiding this comment

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

Wow, looks like the describe on line 917 wasn't closed - good catch!

Copy link
Author

@brunorzn brunorzn May 23, 2018

Choose a reason for hiding this comment

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

Yeah I had to like triple check this to be sure it wasn't me ;) (basically, eslint caught it, not me)

@brunorzn
Copy link
Author

Thanks @hwillson ! Hopefully this will be merged soon !

@hwillson hwillson merged commit 6a851cb into apollographql:master May 23, 2018
@benjamn
Copy link
Member

benjamn commented Jun 6, 2018

This change will cause some problems for #3394, since I have been relying on the reuse of query document objects across repeated cache reads, and this change causes the document to be different every time.

I think a better way to fix the memory leak would be to use a WeakMap for the cache instead of a Map. I'm also worried about the performance implications of all this extra document copying, so I would move to delay publishing this change until we can find a solution that preserves the caching (without memory leaks).

benjamn added a commit that referenced this pull request Jun 6, 2018
After #3444 removed `Map`-based caching for `addTypenameToDocument` (in
order to fix memory leaks), the `InMemoryCache#transformDocument` method
now creates a completely new `DocumentNode` every time it's called
(assuming this.addTypename is true, which it is by default).

This commit uses a `WeakMap` to cache calls to `addTypenameToDocument` in
`InMemoryCache#transformDocument`, so that repeated cache reads will no
longer create an unbounded number of new `DocumentNode` objects. The
benefit of the `WeakMap` is that it does not prevent its keys (the
original `DocumentNode` objects) from being garbage collected, which is
another way of preventing memory leaks.  Note that `WeakMap` may have to
be polyfilled in older browsers, but there are many options for that.

This optimization will be important for #3394, since the query document is
involved in cache keys used to store cache partial query results.

cc @hwillson @jbaxleyiii @brunorzn
benjamn added a commit that referenced this pull request Jun 6, 2018
After #3444 removed `Map`-based caching for `addTypenameToDocument` (in
order to fix memory leaks), the `InMemoryCache#transformDocument` method
now creates a completely new `DocumentNode` every time it's called
(assuming this.addTypename is true, which it is by default).

This commit uses a `WeakMap` to cache calls to `addTypenameToDocument` in
`InMemoryCache#transformDocument`, so that repeated cache reads will no
longer create an unbounded number of new `DocumentNode` objects. The
benefit of the `WeakMap` is that it does not prevent its keys (the
original `DocumentNode` objects) from being garbage collected, which is
another way of preventing memory leaks.  Note that `WeakMap` may have to
be polyfilled in older browsers, but there are many options for that.

This optimization will be important for #3394, since the query document is
involved in cache keys used to store cache partial query results.

cc @hwillson @jbaxleyiii @brunorzn
benjamn added a commit that referenced this pull request Jun 6, 2018
After #3444 removed `Map`-based caching for `addTypenameToDocument` (in
order to fix memory leaks), the `InMemoryCache#transformDocument` method
now creates a completely new `DocumentNode` every time it's called
(assuming `this.addTypename` is true, which it is by default).

This commit uses a `WeakMap` to cache calls to `addTypenameToDocument` in
`InMemoryCache#transformDocument`, so that repeated cache reads will no
longer create an unbounded number of new `DocumentNode` objects. The
benefit of the `WeakMap` is that it does not prevent its keys (the
original `DocumentNode` objects) from being garbage collected, which is
another way of preventing memory leaks.  Note that `WeakMap` may have to
be polyfilled in older browsers, but there are many options for that.

This optimization will be important for #3394, since the query document is
involved in cache keys used to store cache partial query results.

cc @hwillson @jbaxleyiii @brunorzn
hwillson added a commit that referenced this pull request Oct 25, 2018
This helps avoid the related memory leak issue identified
in #3444.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants