colyseus icon indicating copy to clipboard operation
colyseus copied to clipboard

Allow to limit the number of rooms a process can spawn

Open endel opened this issue 7 years ago • 9 comments

To prevent the process from consuming too much CPU and slowing down all of its spawned rooms, it would be best to have a way to limit the number of rooms a process can spawn.

(https://discuss.colyseus.io/topic/11/about-setting-number-of-rooms-for-a-registered-room-on-the-server/4)

endel avatar May 18 '18 17:05 endel

Hi Endel

Any updates on this one ?

TyrusShan avatar Jun 19 '18 08:06 TyrusShan

Syntax suggestion by @oyed

gameServer
  .define('my-room', MyRoom)
  .filterBy(['id'])
  .maxInstances(1);

endel avatar Nov 01 '19 13:11 endel

Use-case for context:

gameServer.define('my-room', MyRoom).filterBy(['id']).maxInstances(1)

maxInstances will take in to account any filters defined by filterBy to determine a count of existing Room Instances. Using the Room definition above:

Client A: .joinOrCreate('my-room', { id: 123 }): Succeeds in creating and joining the Room, then makes it private/locks it Client B: .joinOrCreate('my-room', { id: 123 }): Fails and the Promise is rejected (Previously created instance is private, and only 1 instance can exist with the same id option) Client C: .joinOrCreate('my-room', { id: 456 }): Succeeds in creating and joining the Room

devlsh avatar Nov 01 '19 13:11 devlsh

Did this ever get addressed?

LukeWood avatar Apr 12 '23 02:04 LukeWood

Hi @LukeWood,

It was not addressed yet. @MateusMendesSantana did propose a solution in the past that should work on single-process environments.

I believe a modified version of that could suit environments with more processes as well. This hasn't been prioritized because when you use multiple processes, new rooms are created on the process with the least amount of rooms spawned. Adding more processes will make sure the load is distributed among them.

The feature is still valuable if all available processes are "full", though. So the game doesn't become unplayable due to competing CPU usage.

This won't land on 0.15 yet, but it's an interesting feature to land later!


I'm expecting the server to throw a MatchMakeError with a special error code for the client when the "create" request can't be handled by any of the available processes - so the client can display a proper error message for the user.

endel avatar Apr 12 '23 03:04 endel

Thanks for the information @endel ! I'll see what I can do on my end, and then let you know how I solve the problem!

LukeWood avatar Apr 12 '23 04:04 LukeWood

So to move forward with the proposed solution n @endel do we need to use Redis?

LukeWood avatar May 16 '23 17:05 LukeWood

This is a very hard problem to solve. When you have multiple users hitting different servers at the same time, they both try to create the room at the exact same time.

You'd need some form of interlocking, using REDIS to get a semaphore value for room creation. But, you can't keep that semaphore for very long, because room creation can take some time. So, you've got to create a placeholder room in redis, that lets other servers know that the room is being created. But since the room doesn't exist, it can't necessarily direct players to it just yet. You need a mechanism for all of those players to wait for the room creation to complete, and then they can join.

Honestly, we really need single instance rooms, too. But we don't need colyseus to change to do it.

One approach:

  • Instead of doing joinOrCreate, use matchmaker data
  • check if room exists in matchmaker data
  • if exactly one does great, send roomId to client, and client joins
  • if more than one does, select the one that was created first, and return to client.
  • If it does not, create the room. Put a timestamp in metadata (if we cannot access creation timestamp)
  • wait for room creation to complete
  • check for duplicate rooms. If multiple rooms were created, select the one created first and return to client
  • destroy room that was not selected, if necessary

Easier approach:

  • check for other rooms during onCreate of the room itself.
  • Use a dedicated redis entry for the "filterBy" data, and using HINCR on REdisPresense as a semaphore, and throw if room already exists.
  • client would need to catch the thrown error and retry joining. When it retries, it should find the room that already exists.

hunkydoryrepair avatar Nov 03 '23 19:11 hunkydoryrepair

In our project, we actually implement this using RedisPresence now in the room onCreate. Using hincrby we can do an interlocked update and check result to determine if the max number of rooms for it's type is created and we THROW if the count is exceeded (need to hincrby to undo the first in this case)

hunkydoryrepair avatar Jan 12 '24 07:01 hunkydoryrepair