discordjs / discord.js

A powerful JavaScript library for interacting with the Discord API
https://discord.js.org
Apache License 2.0
25.36k stars 3.97k forks source link

Iterating through client.users to get presences causes 100% CPU #4844

Closed JMTK closed 3 years ago

JMTK commented 4 years ago

Please describe the problem you are having in as much detail as possible: I believe this is related to https://github.com/discordjs/discord.js/issues/2809 - When trying to filter/iterate the client.users.cache, the CPU spikes to 100% and becomes unresponsive. Eventually the bot goes offline due to being unable to process anything. My use case was trying to find which percentage of users were offline out of all of the users I had in the cache.

image Looking through the User.js class, it should stop on the first match in a guild, however each shard has ~2100 guilds, so I think this loop can add up...

Correct me if I'm wrong on this math, but the worst case scenario could theoretically be (2100 guilds x 252583 users = 533 million iterations) I don't think this would be the common case for each user->guilds->presences match. I currently have ~44 presences per guild, and according to Stackoverflow , the Map.has function is O(1).

Include a reproducible code sample here, if possible:

//need large user count
mybot.users.cache.filter(u => u.presence.status === 'offline')

Further details: Users Count Avg. Presences Per Guild Guild Count

qwright10 commented 4 years ago

This is completely expected. When you do something in node that requires a lot of "processing power," you CPU usage should and usually will jump up to 100%.

JMTK commented 4 years ago

It's not that I'm surprised that it's 100% CPU, it's that I don't think the current code for accessing a user's presence is scalable in its current state, because it's going to a Guild's presence cache to return it for each user. Before looking at the code, my original assumption was that I would be looping ~252000 times, not 500 million(per shard)

monbrey commented 4 years ago

Presences are Guild-bound. The Discord API does not provide a User.presence directly, hence the getter instead finds one from a Guild.