How to share state between Phoenix Channel Topics?

I’m trying to share a bit of state between Channel topics and I’m finding that socket.assigns actually isn’t shared between 2 different sockets on the same topic if the sockets are from different clients respectively.

What I’m trying to achieve is to assign a unique match ID to each new client that connects to an arena:lobby channel, when they request it. When this happens, the arena:lobby handle_in callback increments a counter on socket.assigns, so that it knows that it has handed out that number already.
Then, when a new client joins arena:lobby, and requests a match ID, they should get a different number (incremented from the previous).

I was a bit surprised to find out that every new client got a socket with an empty socket.assigns so that the state of previously joined users can’t be persisted.

Is using socket.assigns the wrong place for this?

What is a better place? Should I use con_cache or ETS? just to store this match counter?

thanks!

1 Like

Each Channel has independent state. This is important for resiliency, because two Channels cannot affect each other accidentally.

You could use any sort of global state to manage the match. I would likely reach for a GenServer to start with, and use something else if performance is a problem.

If you have a single production server, then it’s easy to manage the matches “globally” because there’s a single source of truth by nature of a single server. If you have multiple servers, then you’re getting into classic distributed systems problems. In that case, it’s easiest to use an external datastore to manage match information.

4 Likes

Thanks!!

My understanding is that Discord for example uses one GenServer per Discord server. GenServer is in a sense single threaded. So inside GenServer you could safely increase a number and give unique ID for each connection. Another possibility if you only care about uniqueness is of course use some globally unique ID like UUID or Snowflake. Snowflake is 63 bit ID (top bit is not used) used by Twitter. In Snowflake every ID generator has its own ID and that ID is embedded into Snowflake ID so they never collide.

2 Likes

+1 for snowflake GitHub - blitzstudios/snowflake: A distributed Snowflake generator in Elixir.

sounds like the exact use case for it…

You could use an atomic or counter.

I like to point out that will only work with single node. If you are going to run multiple nodes then you need globally unique Id or have single node that will generate you ids.

1 Like