Skip to content

@urql/vue: memory leak in Nuxt 3/4 server #3832

@gianlucadifrancesco

Description

@gianlucadifrancesco

Describe the bug

Hi everyone!

I'm using @urql/vue a lot in Nuxt 3 and 4, and I've noticed there's a memory leak on the Nuxt server when awaiting the useQuery.

My suspicion is that there's something wrong with wonka (or how it's handled), where the "await" in await useQuery() makes some observables remain stuck in memory forever.

I've created a minimal reproduction with both Nuxt 3 and 4, only adding the urql plugin and the PokeAPI to test API calls.
There, you'll find the steps to reproduce the leak and debug with VSCode:

  • Open nuxt-3 or nuxt-4 with VSCode.
  • Install the dependencies with pnpm i.
  • Open the Run and debug section and start Build & Preview, which will build and run in "production" mode.
  • After Nuxt has built and started the server, expand "Call stack" and select the more nested item (nuxt.mjs -> index.mjs).
  • Below, in "Realtime performance", select all items.
  • Open localhost:3000 and keep refreshing the page many times. To be faster, in Chrome you can select the URL bar and press cmd + enter many times (or keep pressing them) to open new pages and trigger many API calls.
  • Now you should be able to see the memory leak, where the Resident Set Size is very high, and becomes higher and higher when you trigger many API calls simultaneously. It's also much higher than Heap Total.
  • If you remove await from the useQuery, the leak will not occur.

You should see something like this:

If you keep opening (or refreshing) hundreds of tabs, the leak can easily go to ~200/300mb. If you stop for a while, the leak might disappear, but in our production scenario this might never happen.

We're using it in production, and we have around ~20/30 requests per second (rarely 0-10 req/s), and with this leak our memory can easily reach ~500mb in a couple of days.
As a workaround, we've currently removing await from the most triggered useQuery.

What do you think about it? Is there a way to fix it?
I'll be glad to help!


These are the results using clinic doctor: it seems like the event loop is accumulating, as well as the Resident Set Size.

Debugging page localhost:3000/ (fetching list data):
clinic doctor --on-port 'autocannon -c 5 -a 100 localhost:3000' -- node .output/server/index.mjs

Debugging page localhost:3000/{id} (fetching single pokemon data):
clinic doctor --on-port 'autocannon -c 5 -a 100 localhost:3000/1' -- node .output/server/index.mjs

Observations:

  • List Data (/): The response payload is only ~43kb, but the memory leak quickly reaches ~400mb under load.
  • Single Item Data: The response payload is just ~1kb, and the leak still increases linearly, reaching~150mb.

The nature of the leak might be related to how arrays and nested arrays/objects (which are abundant in the list data) are processed. The issue could also be related to a wrong cleanup process when URQL checks or parses document fields, such as __typenames.


Reproduction

https://github.com/gianlucadifrancesco/urql-server-mem-leak-nuxt

Urql version

@urql/vue: v2.0.0

Validations

  • I can confirm that this is a bug report, and not a feature request, RFC, question, or discussion, for which GitHub Discussions should be used
  • Read the docs.
  • Follow our Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions