socketio / socketio/socket.io

Better network scaling with adapters

Open
#5,226 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
TypeScript
Stars
63.2k
Forks
10.3k
Avg merge
11d 20h
Merged PRs (30d)
2

Description

Is your feature request related to a problem? Please describe.
The way adapters work, every message gets broadcast to all instances of the service. This represents a scalability problem, especially in scenarios with a lot of instances and relatively small rooms.

We use the redis adapter, and one of our services is very inefficient due to this mechanic. Using round numbers to simplify:
Lets say our room size is 10.
Each instance right now is handling 100 clients.
We have 100 instances of the service running.
We have a total of 1K rooms with clients connected at any given time.
If you assume the best case scenario, where each client in a instance belongs to a different room, then, on average, 90% of messages the instance gets from the adapter are discarded, due to no clients in that room.

This is specially problematic since scaling the service up ends up in a diminishing returns mode.
If you double the amount of instances of your service, each one will handle half the client connections, but will still need to handle all messages from every room. Eventually (as in our case), you end up with a ton of instances of the service, with very few clients, where most of the work is just reading from redis and throwing it away.

Describe the solution you'd like
A way for adapters to only listen for messages of rooms for which they have clients connected.

Describe alternatives you've considered
For redis in particular, I've looked into reimplementing the adapter to do one subscription per room, but there is a trade-off. Per redis docs for PUBLISH:

Time complexity:
O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).

I understand why the default is what it is, based on that. I expect the most common use case to have a lot less instances. So it makes sense to use a single subscription.

But it would be nice to have a different mode, where you trade off having a lot more subscriptions, for more granular message broadcasting.
We scale this particular redis instance due to network usage, not cpu or memory.

Another option is to do sticky sessions based on what room we expect the client to be, but this breaks in scenarios where your services scales up and down, as the slot for any given session might change once new instances are spun up.

Additional context
I checked the rest of the adapters, and it looks like they all share this behavior. That's why I'm opening the issue here and not on the redis-adapter repo, as this might be something that needs to be addressed at the adapter definition / features level

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No file or test is named. Start by reviewing the adapter definition/features area and comparing the shared behavior across adapters described in the issue. Done means an adapter-level approach or mode is specified that limits message delivery to rooms with connected clients while accounting for the subscription trade-offs.

Written by the indexing model from the issue text.

Assessment

Tech stack
redis, typescript
Domain
distributed-systems, networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.