msw: "Warning: Possible EventEmitter memory leak detected" when using with NextJS

Describe the bug

I’ve implemented client and server side mocks as per the docs. Using GraphQL queries and mutations in a NextJS application which I’ve bootstrapped from _app.tsx which is about as global as I can get without a custom server.

With each save the application is re-compiled and refreshed in the browser.

Beyond the performance details listed here, the isomorphic mocks are working nicely.

Server side: Depending on how many routes are visited, or how many changes are made. MSW pretty quickly stops responding and warns of a memory leak with too many open handlers, message from server-side console as below:

“MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [IncomingMessage]. Use emitter.setMaxListeners() to increase limit”

This doesn’t happen with mocks off.

Client In the browser, after navigating to about 5 different routes, the browser stop responding. Chrome is timing out completely, doing nothing at all on route changes. The message in Chrome is:

“waiting for available socket”.

When i use browser tools to end the service worker, normal performance resumes.

Problem doesn’t happen with mocks off.

In NextJS Dev mode, there is hot module reloading going on every save… I think this is where the problem stems from as this uses sockets.

I think there’s an issue with the service worker and the socket reloads.

Once I kill the service worker, the server-side resumes - just with the memory leak warning.

Environment

  • msw: 0.29.0
  • nodejs: 14.17.0
  • npm: 6.14.13
  • next: 10.2.3

Please also provide your browser version.

Chrome: Version 91.0.4472.101 (Official Build) (x86_64)

To Reproduce

Steps to reproduce the behavior: 1 - Load multiple, routes in a NextJS app 2 - after 5 different route loads there are already 1 end listeners 3 - App performance drops to unusable 4 - Use application panel to end the service worker in the browser & app responds as normal

Expected behavior

No memory leaks

Screenshots

If applicable, add screenshots to help explain your problem.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 16
  • Comments: 26 (11 by maintainers)

Most upvoted comments

The fix has been released in 0.32.1 (see the release notes). Could you please update and let us know if the issue is gone?

Looked into this as well. Here’s a reproducible test case: https://github.com/webpro/msw-next

To reproduce the issue:

  • npm install && npm run dev
  • Click a few times to switch pages
  • Try to refresh: page keeps loading indefinitely
  • Stop service worker from Chrome dev tools, and things start working again

Seeing suspicious logs for requests such as:

[MSW] Warning: captured a request without a matching request handler:

  • GET /_next/static/development/_devPagesManifest.json
  • GET /_next/static/webpack/4db3df89d55dadb9c978.hot-update.json
  • GET /_next/webpack-hmr
  • GET /_next/static/chunks/pages/index.js

In another application it also captures requests for images (they are served from the local public folder).

Tried a few versions of Next.js (v9.2 v10.2 v11.0) - all the same issue.

Even when using another hostname and port for the requests (added to /etc/hosts), it looks like it still starts capturing all requests from localhost (but also correctly captures the request for the other domain).

Then I tried to downgrade msw. It seems this leak is introduced in v0.25.0, as things start to work properly with v0.24.4 (and lower).

So to get a working version in the repo above, use npm install msw@0.24.4

Edit: also when downgrading msw to v0.24.4 in a pretty large project with a custom msw setup, the memory leak is no longer present.

I’ve released the fix in @mswjs/interceptors@0.12.3 (Release notes). Could somebody please use that version and verify that the memory leak in Node.js is resolved by this fix?

I’ll prepare a pull request that cleans up the attached event listeners from the IncomingMessage as a part of the getIncomingMessage function. I’ve tested this change locally and confirm that res._events (stream._events in the latest interceptors) is empty once the function is done extracting the response body.

My colleague noticed a workaround:

image

Check the “Update on reload” checkbox.

This prevents the worker from stalling the application… but doesn’t solve the underlying problem.

The event listener leak has been fixed in #822 and the development server being not responsive has been addressed in #834. This issue should be resolved now.

Please update to msw@0.33.1 and let me know if you can still reproduce this. I’ll close the issue until further notice, as I can’t reproduce any issues when working with NextJS now.

Hey @JReinhold, the workaround with NextJS is to stop the worker every time the MyApp Component (_app.tsx) is unmounted:

useEffect(() => {
    worker.start();
    return () => {
      worker.stop();
    };
  });

I am not that confident because I was concerned on a few race conditions for http requests, but I have tested many route changes + hot reload, and it all seems to work fine. The requests are also being mocked correctly in my case.

You could try the router.events, but I found them to be less reliable than useEffect.

However, after a bit more investigation I was able to solve my issue by stopping the worker (worker.stop) every time there is a client-side route change with NextJS. Then I need to start the worker (worker.start) again before the component mounts. It’s a bit more work to setup MSW with NextJS but it works for now.

@andrelas1 can you in the meantime share how you built your workaround in practice?

I will try to find more time to see if I can make a contribution to msw to avoid that extra setup with NextJS.

That would be amazing, @andrelas1. Let us know what you find, perhaps we can account for this on the MSW side, or at least in the official MSW NextJS usage example. Thank you.

Hey @kettanaito I was facing that same issue but after trying the @mswjs/interceptors@0.12.3, it seemed to have solved the issue!

I will keep working with the new version and if it pops something not expected, I will let you know.

I guess we just need to wait for the release of the new msw version 😃

Thanks for the fix!!

Can you try a local build of interceptors with the event listeners removed in getIncomingMessageBody

Sorry I didn’t have the time to try this, but thank you so much for your effort to fix it!

If what I found is correct, should this be better reported as an issue of mswjs/interceptors, instead of this repository ?