Skip to content

[Nuxt] Huge memory leak during SSR cycle #206

@adamlachmann

Description

@adamlachmann

Version

vue-final-modal: v2.4.1
vue: v2.6.12
nuxt: v2.15.8 (It's likely to happen in any SSR context)

OS

Mac

Reproduction Link

Apologies, I currently do not have a time capacity to provide a repo, nor to fix the issue - I'm in a bit of a hellish crunch right now. I did however found the the culprit.

Steps to reproduce

  1. Install the plugin
# plugins/vue-final-modal.js
import vfmPlugin from 'vue-final-modal/lib'
Vue.use(vfmPlugin)
  1. Create a component that utilises vue-final-modal and renders it during SSR cycle (no client-only). Props, including ssr are irrelevant.
  2. Use Chrome dev tools to profile apps memory
yarn build && node --inspect node_modules/.bin/nuxt start
  1. Load test the app with siege or ab
ab -c 50 -n 1000000 -k http://127.0.0.1:3000

What is Expected?

App won't run out of memory

What is actually happening?

App will run out of memory (4gb) :)

Cause of the issue

VueFinalModal.vue saves the reference to component instance in created hook:

created() {
this.api.modals.push(this)
},

As per vue docs, during SSR cycle, only two lifecycle methods are executed - beforeCreate and created - meaning running code that produces side effects inside of those methods will create memory leaks, given that beforeDestroy and destroy won't be run to perform cleanup.

Because this.api is a singular instance initialised during server boot in the plugin file (not on each request), each time VueFinalModal is rendered during SSR, its instance is added to the api.modals and kept there indefinitely - vide the memory leak.

I confirmed this by removing this line and performing load tests again - no more memory leaks.

Temporary fix

If VueFinalModal is not rendered server-side, it will not register in modal api and not produce any memory leaks.

<client-only>
   <vue-final-modal ... />
</client-only>

A proper, simple fix I imagine would be to conditionally register modal in the API only on the client. In the long run however, I do worry whether using global instance of api isn't playing with fire - at least in SSR usages.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions