Discord.js: Sharding gives "Possible EventEmitter memory leak detected" warnings

Created on 11 Jul 2020  Â·  1Comment  Â·  Source: discordjs/discord.js

Please describe the problem you are having in as much detail as possible:

The warnings below are constantly printed out every once in a while (it seems to be fairly random intervals). However, the numbers are always the same (in this case, 32 and 21).

(node:32) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message listeners added to [process]. Use emitter.setMaxListeners() to increase limit
(node:21) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message listeners added to [ChildProcess]. Use emitter.setMaxListeners() to increase limit

Include a reproducible code sample here, if possible:

// "index.js" would be any code that spawns a Client.

import { ShardingManager } from 'discord.js';

(async () => {
  const manager = new ShardingManager('./index.js', { token: process.env.DISCORD_BOT_TOKEN });

  await manager.spawn().catch(console.error);

  console.log(`Spawned ${manager.shards.size} shards.`);
})();

Further details:

I believe the error is coming from %project_path%/node_modules/discord.js/sharding/ShardClientUtil.js lines 35 & 47 as it listens to both of the events that are in the warning. I don't listen to the message event in either process or ChildProcess at any time, and these warnings are never seen when I don't use sharding. This leads me to believe that's it's caused by the two lines I mentioned above.

It seems like ShardClientUtil is re-created seemingly at random - the only time I see a new instance being created is when a new Client is created, so maybe sharding re-spawns Clients for some reason or another without destroying the old one which is causing this?

  • discord.js version: 12.2.0
  • Node.js version: 14.3.0 (also tested on 14.4.0, 12.16.0, & 12.4.0)
  • Operating system: Windows 10 64-bit
  • Priority this issue should have – please be realistic and elaborate if possible: Low. This isn't a huge deal and seems to clean itself up over time so it's just kind of annoying to look at in the console.
  • [x] I have also tested the issue on latest master, commit hash: f9f3661
unverified bug

Most helpful comment

Update. I've found what's causing it.

Running broadcastEval in quick succession (11+ times) (eg. in Promise.all) will show the warning.

line 148 in ShardClientUtil.js has parent.on('message', listener);, and it removes the listener once it finishes. However, running it enough times at once will show the warning as most are still performing tasks so it just keeps adding on the listener.

Here's an MRE:

// "client" is a Discord client.

(async () => {
  await Promise.all(Array.from({ length: 15 }, () =>
    client.shard.broadcastEval(`(async () => {
      await new Promise(r => setTimeout(r, 5000));
    })();`);
  ));
})();

This will run broadcastEval 15 times where each just waits 5000 ms before finishing.
As the "issue" isn't really an issue that has any large negative impact it's probably fine to keep it as it is.

A way to fix it could be using a Map (or Collection) with the script as the key and the value as like { resolve, reject } (both as their respective functions). This way, you only need to listen to the message event on process once, and you can handle them as they come in.

>All comments

Update. I've found what's causing it.

Running broadcastEval in quick succession (11+ times) (eg. in Promise.all) will show the warning.

line 148 in ShardClientUtil.js has parent.on('message', listener);, and it removes the listener once it finishes. However, running it enough times at once will show the warning as most are still performing tasks so it just keeps adding on the listener.

Here's an MRE:

// "client" is a Discord client.

(async () => {
  await Promise.all(Array.from({ length: 15 }, () =>
    client.shard.broadcastEval(`(async () => {
      await new Promise(r => setTimeout(r, 5000));
    })();`);
  ));
})();

This will run broadcastEval 15 times where each just waits 5000 ms before finishing.
As the "issue" isn't really an issue that has any large negative impact it's probably fine to keep it as it is.

A way to fix it could be using a Map (or Collection) with the script as the key and the value as like { resolve, reject } (both as their respective functions). This way, you only need to listen to the message event on process once, and you can handle them as they come in.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Lombra picture Lombra  Â·  3Comments

smchase picture smchase  Â·  3Comments

BrandonCookeDev picture BrandonCookeDev  Â·  3Comments

DatMayo picture DatMayo  Â·  3Comments

PassTheMayo picture PassTheMayo  Â·  3Comments