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 memory leak in Fastboot #47
base: master
Are you sure you want to change the base?
Conversation
The Ember module here is shared across app instances in a Fastboot environment. That means that each new app instance gets the same `Ember` reference here. Because we saved a reference to the old `onError` function, and because the `onError` function closes over a reference to the fastboot and rollbar services, that means that each time this instance initializer ran it retained a reference to an entire app instance that should have been GC'd. The new `onError` retains the old `onError`, which retains the fastboot & rollbar services, which retain the app instance containers they were instantiated through, which retains basically an entire copy of the app. Removing the reference to the old `onError` here stops the leak by breaking that retainer chain.
Thanks, Dave for pointing this out! :) There is one issue with this solution. If we remove the old
and I will install this addon, it will overwrite the entire logic. it won't be possible to define your own logic, and it can produce conflicts with other add-ons using this behavior. Maybe the fix would be to move it to |
Yea, I was trying to think of a way to preserve the old behavior without closing over the reference and came up empty, but I didn't think of just changing this to an initializer. That makes much more sense, since |
Yes... but now we would have think how to get owner in |
Hmm, right. Seems like, because Rollbar requires instantiating their client class (i.e. it's not just a global state thing, you could have multiple rollbar clients), and Ember's One option might be to create a "global" rollbar instance for fastboot environments. In other words, if we could avoid looking up the fastboot and rollbar services here, and convert this to an initializer (vs instance initializer), it should be okay, since it won't retain any references to app instance containers & will only run once. But - we can't lookup the consuming app's Rollbar config without access to a container, which means retained references. I honestly don't see a way around this without requiring consuming apps that use fastboot to define their own initializer so they can import their Rollbar config. Perhaps we have to settle for that, and disable this initializer in fastboot mode (which, of course, we couldn't check via the service, we'd have to do something like |
@davewasmer are Browser-Only or Node-Only Initializers an option to address your last point? |
Yea, we could wrap the initializer in a Fastboot check, but the downside there is that you lose Rollbar reporting in the Fastboot environment. That's what I've done on our fork (had to do something since this leak was crashing our fastboot app every couple hours), but wasn't sure that's what you'd want to do. Happy to PR that change if you're cool with it though. |
🤔 can it be moved from an instance-initializer to an initializer? |
@sdhull unfortunately not :/ But maybe we can save a state on an application object (not appInstance) if the error handler has been already registered to not reregister it? What do you think? Or we can use a shoebox to pass the state |
The Ember module here is shared across app instances in a Fastboot environment. That means that each new app instance gets the same
Ember
reference here. Because we saved a reference to the oldonError
function, and because theonError
function closes over a reference to the fastboot and rollbar services, that means that each time this instance initializer ran it retained a reference to an entire app instance that should have been GC'd.The new
onError
retains the oldonError
, which retains the fastboot & rollbar services, which retain the app instance containers they were instantiated through, which retains basically an entire copy of the app.Removing the reference to the old
onError
here stops the leak by breaking that retainer chain.