Skip to content

SSR memory leak in poll method #2606

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

Closed
ronald-d-rogers opened this issue Feb 6, 2019 · 3 comments · Fixed by #2875
Closed

SSR memory leak in poll method #2606

ronald-d-rogers opened this issue Feb 6, 2019 · 3 comments · Fixed by #2875
Labels

Comments

@ronald-d-rogers
Copy link
Contributor

ronald-d-rogers commented Feb 6, 2019

Version

3.0.2

Reproduction link

https://jsfiddle.net/ronald_d_rogers/5mxdpkLj/2/

Steps to reproduce

Make router-view appearance conditional based on some variable (or simply have it not appear):

<div id="app">
  <div v-if="condition">
    <router-view />
  </div>
</div>

Ensure that the component that matches the route has a beforeRouteEnter guard, where a function is passed to the next(...) method (e.g. next(vm => {}):

export default {
  beforeRouteEnter(to, from, next) {
    // Poll happens only if you pass in a function to next(...)
    next(vm => {})
  }
}

Visit that route.

curl http://localhost:8080/route

A poll will happen infinitely until router-view is created.

In a typical SSR application where an app is created per request, it will never be created.
The end result is a memory leak with an event loop filled with poll's every 16ms.

In the reproduction JS Fiddle, a leak occurs every time you press the SSR Request button.
If you set dontLeak to true, the leak stops.

The leak can be verified in the JS Fiddle by clicking on the SSR Request button a few times (say 20 times), opening Chrome Developer Tools, going to the Performance tab, and in the recording looking at the contents of "Frame".

capture

Here is an example repo as well:
https://github.com/ronald-d-rogers/vue-router-ssr-memory-leak

What is expected?

A new poll does not recurse infinitely for every SSR request.

What is actually happening?

A new poll recurses infinitely for every SSR request.

@ronald-d-rogers
Copy link
Contributor Author

ronald-d-rogers commented Feb 7, 2019

After perusing the code-base of vue-server-renderer and vue-router these are the options that immediately come to mind:

  1. Simply only allow the poll method to iterate a max number of times equal to some time limit (say 10s). In development, warn(...) if it reaches the time limit.

  2. First, modify the isActive IsValid used in vue-router's poll (which currently just checks to see if the route is valid, and stops the poll if it isn't) to also check if the app is valid (e.g. ._isBeingDestroyed === false). Modify vue-server-renderer to pass the app back to the developer when the request is complete so that they can optionally $destroy it (not everyone creates an app per request). So the promise that gets resolved in the server's entry would have to support two APIs, one to deal with when we resolve with just app (current API), one that deals with when we resolve with something like the following { app, cb: (app) => { app.$destroy() } }. I've tested this and it seems to work, but it would involve changes accross packages, though neither package would break if a project didn't have both changes.

I'd be happy implementing these or any other solution.

It might be better to modify instances to allow you to register callbacks when an instance is registered. This would not fix the memory leak but might preferable to a poll, though maybe not.

@posva
Copy link
Member

posva commented Aug 1, 2019

Hey @ronald-d-rogers sorry for the delay. Is vuejs/vue#9479 the solution you came up with at the end? or do we still need to change anything in Vue Router after that PR is merged?

@ronald-d-rogers
Copy link
Contributor Author

ronald-d-rogers commented Aug 2, 2019

You would have to merge the PR in Vue, then one to check if the app is being destroyed here:

const isValid = () => this.current === route

Let me go ahead and create a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
2 participants