Skip to content

SSR Memory Leak with Legacy API #920

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

Open
5 tasks done
DoubleJ-G opened this issue Feb 1, 2022 · 35 comments · Fixed by #1479
Open
5 tasks done

SSR Memory Leak with Legacy API #920

DoubleJ-G opened this issue Feb 1, 2022 · 35 comments · Fixed by #1479
Assignees
Labels
legacy ❗ p4-important Priority 4: bugs that violate documented behavior, or significantly impact perf

Comments

@DoubleJ-G
Copy link

Reporting a bug?

Using vite-plugin-ssr to run Vue 3 with server side rendering, leaving the legacy api enabled will cause memory to slowly increase with every request.

Expected behavior

Memory does not unexpectedly keep increasing.

Reproduction

I have reproduced a minimal example here https://github.com/DoubleJ-G/vue-i18n-memory-leak

I have also created a branch legacy-disabled to demonstrate the issue does not happen with it disabled.

System Info

System:
    OS: Linux 5.13 Pop!_OS 20.04 LTS
    CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
    Memory: 1.18 GB / 15.55 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 16.13.0 - ~/.nvm/versions/node/v16.13.0/bin/node
    Yarn: 1.22.17 - ~/.nvm/versions/node/v16.13.0/bin/yarn
    npm: 8.1.0 - ~/.nvm/versions/node/v16.13.0/bin/npm
  Browsers:
    Chrome: 91.0.4472.77
    Firefox: 93.0
  npmPackages:
    @vitejs/plugin-vue: ^1.9.3 => 1.10.2 
    @vue/compiler-sfc: ^3.2.20 => 3.2.29 
    @vue/server-renderer: ^3.2.20 => 3.2.29 
    vite: 2.6.7 => 2.6.7 
    vite-plugin-ssr: ^0.3.31 => 0.3.51 
    vue: ^3.2.20 => 3.2.29 
    vue-i18n: ^9.2.0-beta.30 => 9.2.0-beta.30

Screenshot

Running 20,000 requests with legacy enabled:
legacy-enabled

Running 20,000 requests with legacy disabled:
legacy-disabled

Additional context

From some memory debugging I pinned it down to vue-i18n.cjs.js line 1549 which is the beforeCreate() hook. I could not pin point it further, but if I comment out the hook the issue also seems to be resolved. I believe this may be an issue because Vue docs state unmounted() is not called on the server side, which seems to be were the cleanup should run.

Validations

@DoubleJ-G DoubleJ-G added the Status: Review Needed Request for review comments label Feb 1, 2022
@kazupon kazupon added Type: Bug Bug or Bug fixes and removed Status: Review Needed Request for review comments labels Feb 2, 2022
@kazupon
Copy link
Member

kazupon commented Feb 2, 2022

@DoubleJ-G
Thank you for your great reporting and your cause researching!

I believe this may be an issue because Vue docs state unmounted() is not called on the server side, which seems to be were the cleanup should run.

As your mentioned, I think that the lifecycle of vue has an effect too.
I'll try to fix it later.

@kazupon kazupon added help wanted Extra attention is needed Priority: High Status: PR Welcome Welcome to Pull Request and removed Priority: High labels Feb 2, 2022
@kazupon kazupon removed Type: Bug Bug or Bug fixes help wanted Extra attention is needed labels Mar 1, 2022
@kazupon kazupon added the 🔨 p3-minor-bug Priority 3: a bug in an edge case that only affects very specific usage label Nov 15, 2022 — with Volta.net
@ivannkf
Copy link

ivannkf commented Feb 15, 2023

I'm having the same issue, but it's not fixed even using the legacy: false option.
I'm also using vite-plugin-ssr

@ivannkf
Copy link

ivannkf commented Feb 15, 2023

I created this reproduction repo with the latest version and legacy api disabled.

https://github.com/ivannkf/memory-leak-reproduction

@chemeng
Copy link

chemeng commented Jun 18, 2023

@kazupon is there any update on this? we are also experiencing the same problem with vite-plugin-ssr and legacy: false

@chemeng
Copy link

chemeng commented Jun 22, 2023

@ivannkf @DoubleJ-G did you figure out a way around this?

@chemeng
Copy link

chemeng commented Jul 10, 2023

is this not expected to be fixed anytime soon I assume?

@ghost
Copy link

ghost commented Jul 26, 2023

Just a bit of follow up. Was concerned by a memory leak on my side as well.

Toggled off "globalInjection" in the vue-i18n plugin and had to import the t method using const { t } = useI18n().
With this, I have no more memory leak.

The only downside is that I had to recheck every single component/page/layout to replace those global $t with the const { t } = useI18n() in my script setup parts.

For the record, I'm using Nuxt 3, and Nuxt 3 is creating new plugins for each requests. Global Injection seems to be kept alive, while components instances are destroyed. That's why this seems to be working.

My Nuxt plugin configuration :

export default defineNuxtPlugin(({ vueApp }) => {
  const i18n = createI18n({
    legacy: false,
    globalInjection: false,
    locale: "fr",
    messages: {
      fr,
    },
  })
  vueApp.use(i18n)
})

Update :
Just so you know, we're running our app on docker, and our node image is node:18.17.0-alpine

@chemeng
Copy link

chemeng commented Jul 26, 2023

@GaetanWi unfortunately we are already using these settings and it still blows up our memory. I'm using this with https://github.com/brillout/vite-plugin-ssr

@brokenfiles
Copy link

brokenfiles commented Jul 30, 2023

@GaetanWi I'm using the exact same config, when I disable the I18n plugin, no leak. But when I stress test with the plugin activated, my memory goes boom. My global injection setting is already turned off.

@ghost
Copy link

ghost commented Jul 31, 2023

@chemeng I'm not using vite-plugin-ssr so I could not help sorry :/
if that could help you, here is what Kael from Vuetify's team had to do to prevent a leak on his vite-ssg app with vuetify vuetifyjs/vite-ssg@ff268ab

Maybe there is something like that to do for i18n that allows you to drop useless listeners ?

@brokenfiles any chance on getting your nuxt config ? How do you use your translation module ? Calling useI18n properly ?

@brokenfiles
Copy link

@chemeng I'm not using vite-plugin-ssr so I could not help sorry :/ if that could help you, here is what Kael from Vuetify's team had to do to prevent a leak on his vite-ssg app with vuetify vuetifyjs/vite-ssg@ff268ab

Maybe there is something like that to do for i18n that allows you to drop useless listeners ?

@brokenfiles any chance on getting your nuxt config ? How do you use your translation module ? Calling useI18n properly ?

Hello, after further research, I found that the problem was indeed with vue-i18n and my installation. But I couldn't find any difference between my Nuxt installation and the doc.

I managed to solve the problem by installing the module (still in beta) recommended by Nuxt (https://nuxt.com/modules/i18n).

Here is my old nuxt.config.ts config :

import { resolve } from 'path';
import { defineNuxtConfig } from 'nuxt/config';
const r = (p: string) => resolve(__dirname, p);

export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt', '@nuxt/image-edge'],
  css: ['assets/scss/layout.scss'],
  app: {
    head: {
     ....
   }
  },
  plugins: [{ src: '~/plugins/auth/index.server.ts', mode: 'server' }],
  runtimeConfig: {
    public: {
     ....
    },
  },
  build: {
    transpile: ['@vuepic/vue-datepicker', 'chart.js'],
  },
  nitro: {
    prerender: {
      routes: ['/sitemap.xml', '/robots.txt'],
    },
  },
  vite: {
    esbuild: {
      tsconfigRaw: '{}',
    },
    resolve: {
      alias: {
        '~': r('.'),
        '~/': r('./'),
        '~~': r('.'),
        '~~/': r('./'),
        '@@': r('.'),
        '@@/': r('./'),
        assets: r('./assets'),
        public: r('./public'),
        'public/': r('./public/'),
      },
    },
  },
});

Here my old i18n.ts plugin :

import { createI18n } from 'vue-i18n';
import { DEFAULT_LOCALE } from '@intlify/core-base';
import { defineNuxtPlugin, useCookie } from '#imports';
import en from '~/locales/en';
import fr from '~/locales/fr';
import es from '~/locales/es';
import it from '~/locales/it';
import de from '~/locales/de';
import ptBr from '~/locales/pt-br';
import { getBrowserLocale } from '~/composables/i18n/useBrowserLocale';

type MasterSchema = typeof fr;
export const AVAILABLE_LOCALES = [
  'fr',
  'en',
  'es',
  'it',
  'de',
  'pt-br',
] as const;

export type AvailableLocale = typeof AVAILABLE_LOCALES[number];
export const I18N_COOKIE_KEY = 'i18n.locale';
export const MASTER_LOCALE_NAME: AvailableLocale = 'en';

export default defineNuxtPlugin(({ vueApp }) => {
  const defaultLocale = getBrowserLocale() ?? DEFAULT_LOCALE;
  const inOneYear = new Date();
  inOneYear.setFullYear(inOneYear.getFullYear() + 1);
  const localeCookie = useCookie(I18N_COOKIE_KEY, {
    expires: inOneYear,
  });
  // Set the default locale in cookies
  if (!localeCookie.value) localeCookie.value = defaultLocale;

  const i18n = createI18n<[MasterSchema], AvailableLocale>({
    legacy: false,
    globalInjection: true,
    fallbackLocale: MASTER_LOCALE_NAME,
    locale: locale ?? defaultLocale,
    missingWarn: false,
    fallbackWarn: false,
    messages: {
      fr,
      en,
      es,
      it,
      de,
      'pt-br': ptBr,
    },
  });

  vueApp.use(i18n);
});

For the fix, I added the i18n-module mentioned earlier.
Here is my i18n config in the nuxt config file :

  i18n: {
    vueI18n: './i18n.config.ts',
    locales: ['fr', 'en', 'es', 'it', 'de', 'pt-br'],
    strategy: 'no_prefix',
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n.locale',
      redirectOn: 'root',
    },
  },

And finally the i18n.confg.ts :

export default defineI18nConfig(() => {
  return {
    legacy: false,
    globalInjection: false,
    fallbackLocale: DEFAULT_LOCALE,
    fallbackWarn: false,
    missingWarn: false,
    messages: {
      en,
      fr,
      es,
      it,
      de,
      'pt-br': ptBr,
    },
  };
});

I hope this helps if people are in the same situation as me.

@ghost
Copy link

ghost commented Aug 1, 2023

Well, I had a small leak also and this was the case. Switching to the nuxt plugin.
Was coming here to tell about this option, but you're 5h faster than me 😂

@brokenfiles in addition to your message, the plugin is in his first release candidate since yesterday

@ghost
Copy link

ghost commented Aug 1, 2023

I believe also that with a global registration, the globalInjection is viable.
As plugins are run once per request, it ended up in multiple injections, but with the vue-i18n being registered on the server once as a module, it should be a lot better now

@kazupon kazupon added ❗ p4-important Priority 4: bugs that violate documented behavior, or significantly impact perf and removed 🔨 p3-minor-bug Priority 3: a bug in an edge case that only affects very specific usage labels Aug 2, 2023 — with Volta.net
Copy link
Member

kazupon commented Aug 2, 2023

Hi there!
Thank you for your feedback!

I fixed this issue with your tips.
You can install and try vue-i18n v9.3.0-beta.25.
If you have time it would be great if you could check it out.

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

Im also facing memory leaks in SSR, trying out v9.3.0-beta.25 as we speak

globalInjection is trueand legacy is also true (default)

@chemeng
Copy link

chemeng commented Aug 2, 2023

It doesn't look it fixed it for me.

@brokenfiles
Copy link

@chemeng @kazupon For me, the memory leaks were not related to the globalInjection setting.
Even set to false, I still had leaks.

@chemeng
Copy link

chemeng commented Aug 2, 2023

@brokenfiles same here, I don't use globalInjection at all.

@brokenfiles
Copy link

Screenshot 2023-08-02 at 11 20 05

Here's the before-and-after difference in memory consumption.
The server was "Killed" every 6 hours, depending on the number of requests.

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

@brokenfiles the after is with v9.3.0-beta.25?

What is your globalInjection and legacy setting?

@kazupon kazupon reopened this Aug 2, 2023
@brokenfiles
Copy link

brokenfiles commented Aug 2, 2023

@brokenfiles the after is with v9.3.0-beta.25?

What is your globalInjection and legacy setting?

No, before.
But globalInjection was set to false, legacy also.

#1479 seems to clean the $i18n object after unmount, but if the globlaInjection is set to false, it does not change anything imo

Update: I miss understood your question "the after is with v9.3.0-beta.25?"
No, after is with the recommended plugin by Nuxt (https://v8.i18n.nuxtjs.org/)

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

@brokenfiles

I have globalInjection true and legacy true (because I have a mixed setup with script setup and Options API)

image

I did a new deploy with v9.3.0-beta.25 at the line. Maybe early to say, but it looks like it's gonna stabilise.

(before the line, I commented some parts of the app, to debug)

@brokenfiles
Copy link

I'll try a stress test with the new release to see if it's fixed

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

Update: I miss understood your question "the after is with v9.3.0-beta.25?"
No, after is with the recommended plugin by Nuxt (https://v8.i18n.nuxtjs.org/)

Im not using Nuxt, im using our in house created SSR package https://github.com/bistroo/vue-ssr

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

@brokenfiles

image

It's not stable. As you can see, after a period it seems to consume memory sadly...

@brokenfiles
Copy link

brokenfiles commented Aug 2, 2023

Update: I miss understood your question "the after is with v9.3.0-beta.25?"
No, after is with the recommended plugin by Nuxt (https://v8.i18n.nuxtjs.org/)

Im not using Nuxt, im using our in house created SSR package https://github.com/bistroo/vue-ssr

This is maybe why you doesn't have the issue.
Unfortunately, I still have the issue even with the latest update (v9.3.0-beta.25).

For the test, I used the --inspect argument from node CLI.
I used siege to simulate a lot of users.
Command : gtimeout 30 siege -c 30 http://localhost:3000

Here's my results (remember that I'm using Nuxt3) :

As you can see, the first snapshot is normal, about 30mb, but after the stress test, the memory explodes and stays high.
Screenshot 2023-08-02 at 11 54 53

Here's the siege results :

Screenshot 2023-08-02 at 11 53 17

I think the issue is not closed yet.

Update: after your last message, you seems to still have the issue!

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

@brokenfiles yep, the issue still exists sadly...

@yooouuri
Copy link

yooouuri commented Aug 2, 2023

globalInjection is true and legacy is also true (default)

Im going the replace every injected $t with const { t } = useI18n() and change globalInjection to false and legacy to false. Then test again. Ill report back.

@kazupon kazupon self-assigned this Aug 12, 2023
Copy link
Member

kazupon commented Aug 13, 2023

@ivannkf

I created this reproduction repo with the latest version and legacy api disabled.

Thank you for your reproduction
And Sorry my late reply 🙇

Your case was not set up for a proudction build in vite. Therefore, the code for vue-devtools is installed.
I recommend using vue-i18n and unplugin-vue-i18n if you want to use your application with vite.
unplugin-vue-i18n optimizes the vue-i18n code for production.

@kazupon kazupon removed the Status: PR Welcome Welcome to Pull Request label Aug 13, 2023 — with Volta.net
Copy link
Member

kazupon commented Aug 13, 2023

Hi! all
As the title says, this issue is a memory leak when using the legacy API (lagacy: true).

I was a bit confused by this current issue as there are multiple cases reported. 😅

If this occurs in the lagacy: false case, please open a new issue with the minimum reproduction code.

@kazupon kazupon added the legacy label Aug 13, 2023 — with Volta.net
@mercs600
Copy link

We have faced memory leaks in SSR as well.
As a result, we had a lot of doubled entries (fetched data) with each request:

Zrzut ekranu 2023-11-28 o 18 58 13

I noticed that disabling @nuxtjs/[email protected] helped.

The solution is weird but works for us:
We realized that we have @nuxtjs/i18n in regular dependencies, not in devDependencies as the documentation suggests.

@BobbieGoede
Copy link
Member

If any of you is still able to reproduce this leak using the latest version of vue-i18n (with or without Nuxt/Nuxt I18n), could you please share an up to date reproduction?

I have tried updating the older reproductions provided here without any luck, I would be happy to take a good look at the memory leak if it still occurs! So far I have been able to eliminate some of the memory leaks in @nuxtjs/i18n but unfortunately it seems some projects still experience leaks which I haven't been able to replicate, so having one would be a big help.

@Evertvdw
Copy link

Evertvdw commented Mar 7, 2024

@BobbieGoede I am also experiencing issues with memory leaks, which started occurring when I updated my project to the latest Vue/Vite version using Quasar. I also had to update vue-i18n when doing that upgrade (from [email protected]), which I first suspected as the culprit.

I tried to make a reproduction for this and found out that it does not seem to be vue-i18n version specific but rather what version of vite is used. Using the exact same version of Vue/vue-i18n/@intlify/unplugin-vue-i18n I get the memory leak with Vite@5 but not with Vite@2.

Probably it has something to do with using the ESM version of this package, as the new project that has the issue is bundled as ESM and the previous one as CommonJS.

Repo for version without memory leak: https://github.com/Evertvdw/memory-leak-quasar-v1

Repo for version with memory leak: https://github.com/Evertvdw/memory-leak-quasar-v2

These repos contain a readme with some instructions/steps for the reproduction.

@Evertvdw
Copy link

I just tried the beta version 10 and the issue mentioned above seems to be fixed there. So that is great :)

@BobbieGoede
Copy link
Member

@Evertvdw
That's great to hear! Sorry I haven't responded after you shared your reproductions, I did take a look but debugging memory leaks is quite tricky 😅

Since version 10 reduces the codebase I hope that makes it somewhat easier to debug memory leaks if they do pop up again 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legacy ❗ p4-important Priority 4: bugs that violate documented behavior, or significantly impact perf
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants