To make Events via WebSockets as efficient as possible, we strive to reduce database operations by using a caching system that immediately tells us, which event is supposed to be sent to which client.
Splitting up the problem
Tackling this problem is best with a bit of divide and conquer, meaning: Instead of tackling the entire problem as a whole, we should divide it into smaller parts first, then tackle those smaller parts.
If we think about it, ~95% of events are emitted in the context of a Guild. Whether it is new messages, guild member updates or changes to channels and channel state (voicechat joins/leaves) - all of these can be traced back to a Guild. However, Guilds are not a fine enough denominator when it comes to determining whether a user should receive an event or not. Think of the case where Guild moderators have a moderator-only channel - it would be bad if the MessageCreate events in that channel were sent to everyone on that Guild.
One level "below" Guilds are Roles. Every user has at least the @\everyone role, and roles with their assigned permissions determine which channels a user can and cannot see.
But there are two slight problems:
Using only roles to determine event-eligibility would make channel/category permission overrides for individual users useless.
The other ~5% of our Gateway traffic are caused by DMs, Group DMs and Inter-User relationships. Roles do not work here either
As such, the second "bin" with which we determine event eligibility should be individual user IDs.
The end result
In the end, we have two "bins" of eligible subscribers for events:
Every user who has a specific Role ID assigned to them
Other, individual users who are also supposed to receive the event
Using a mathematical set like HashSet<T>, we can deduplicate, so that one user doesn't receive an event twice, then send the event to all recipients.
Tracking tasks
[x] Create a way to send events to GatewayUsers from the HTTP API
[ ] Keep track of which users are currently connected to the Gateway
[x] Register users when they connect to the gateway
[x] Register users creating new accounts
[x] Reliably unregister users when they disconnect (or are disconnected) from the gateway
[ ] Handle users deactivating or deleting their account
[ ] Handle cases where a user got removed from the database but is still loaded in memory
[x] Add all of a users' roles' IDs to a Role ID -> User ID mapping
To make Events via WebSockets as efficient as possible, we strive to reduce database operations by using a caching system that immediately tells us, which event is supposed to be sent to which client.
Splitting up the problem
Tackling this problem is best with a bit of divide and conquer, meaning: Instead of tackling the entire problem as a whole, we should divide it into smaller parts first, then tackle those smaller parts.
If we think about it, ~95% of events are emitted in the context of a Guild. Whether it is new messages, guild member updates or changes to channels and channel state (voicechat joins/leaves) - all of these can be traced back to a Guild. However, Guilds are not a fine enough denominator when it comes to determining whether a user should receive an event or not. Think of the case where Guild moderators have a moderator-only channel - it would be bad if the
MessageCreate
events in that channel were sent to everyone on that Guild.One level "below" Guilds are Roles. Every user has at least the @\everyone role, and roles with their assigned permissions determine which channels a user can and cannot see.
But there are two slight problems:
As such, the second "bin" with which we determine event eligibility should be individual user IDs.
The end result
In the end, we have two "bins" of eligible subscribers for events:
Using a mathematical set like
HashSet<T>
, we can deduplicate, so that one user doesn't receive an event twice, then send the event to all recipients.Tracking tasks
GatewayUser
s from the HTTP API