Is it possible to keep assigns in another channels, but in same socket?

channels
phoenix

#1

Hey, I’m writing a WebSocket app in Phoenix.
In one socket I have defined 3 channels.
As I can see I can access socket.assigns in same channel in different &handle_in/3 implementations.
I want to be able to do same with multiple channels in same socket (&join/3 implementations).
What I’m trying to do is to separate API per model, so I have a channel to managing users (register, login, logout …) and other channels. In other channels I want to have access to: socket.assigns.current_user that is set in first channel.
Is it even possible?


#2

I’m… unsure what you are asking, so let me explain how it works to see if it helps. :slight_smile:

When a socket is connected the socket setup module is called, any assigns there stay there (immutable).

When a channel (and topic for that channel) is established it copies the socket object from the socket, assigns and all, and it becomes its own here.

If you register any other topics to that channel, it is still all the same socket objects as that channel, because it is the spawned process that copies the socket, not the topic registrations, they all use the same process.

I ‘think’ you are asking how to see the same assigns from different channels, to do so you set the assigns on the socket connect/2 function. :slight_smile:

If anything else is needed, it might be better to in more detail describe what you are trying to accomplish. :slight_smile:


#3

@OvermindDL1: thanks for fast response

I updated my question.

socket &connect/2 function is called when I execute this:

let socket = new Phoenix.Socket("/ws");

right?

Is so then it does not help.
What I’m doing is:

  1. Connect with Phoenix.Socket
  2. Join users channel
  3. Register in users channel
  4. Logged in users channel - here I have current user (I mean not at first step)

#4

Yes.

If you want to share that logged in user with other channels then you cannot directly (you could send a message though) because all channels are peers, none can touch anything of the others. You instead need to log in the user in the socket then share that auth token with all the channels that are then later created via assigns. :slight_smile:

Or you can pass messages externally, though that would be fighting the design a bit more than you would really want. ^.^


#5

@OvermindDL1: I have a question related to Phoenix.Token API. As I can see first argument could be my endpoint, so this API reads secret key from secret_key_base configuration of my endpoint. I don’t know what should I use as a 2nd argument that’s named salt. Is it a second secret key that should be also complex and long?


#6

The salt is a second secret key but it is per-token rather than system-wide, and it is just to add a bit more randomization so it can be simple, I usually just use something related to the use, so when I stuff in the :account_id I will usually just make it "account_id_salt" or something. ^.^

If you are really paranoid then you could store it in the database or so…


What is the best way to share state between phoenix channels?
#7

Hi !

You are saying that the socket data is copied. So if I want to assign data for a topic only, say “room:123”, I just have to do it in the channel code (like the join or handle_in functions).

And then, my assigns[:stuff] will be different in my room:123 and room:456. So no need to setup an Agent process like I’ve found on internet.

Of course, this works whith the classic JS code : socket.channel().join(), not if I subscribe the token to another topic from the channel code.

Am I right ?

Or does channel "room:*", HelloWeb.RoomChannel mean that every topic matching “room:*” will be in the same process ?

Thank you


#8

Personally I recommend to assign data in Socket, so all socket channels would share its data. For example you could create MyAppWeb.GuestSocket and MyAppWeb.UserSocket. First on connect would require credentials or session token and second would not require anything. In such way you can easily work with multiple Channel and Socket setup.

If I remember correctly:

  1. If you would assign something in MyAppWeb.UserSocket.connect/2 then you would share all data across all Channels in specific Socket.

    You can read more details in Phoenix documentation: Socket params and assigns

  2. If you would assign something in MyAppWeb.UserChannel.join/2 then you would share all data across all handle_* events in specific Channel.

  3. If you would assign something in specific handle_in* event then such data would be available to all other events, but of course only after handle_* event which assigned it has finished successfully. This means that you need to secure other topics by checking if you have assigned required data already and return something like: {"error": 400, "reason": "some event was not yet received"}.

You have one process for every Socket connect and one process for Channel join. It’s why you can’t share data across multiple Channel unless you are assigning it in Socket.connect/2 i.e. before you join Channel. Of course if you are using multiple topics for specific Channel.join/3 then you can’t share data across all of them. Imagine being in lobby with all game players in whole game (i.e. not only in specified lobby with let’s say 10 players limit. That would be really terrible to manage list of users in lobby UI :smile:


Please correct me if I made any mistake as in last time I’m sick, so my brain is not working with highest effective. :077:


#9

Well as I said, I’m trying to figure out how things work “if I want to assign data for a topic only”.
But I thing we have the same understanding of how things work here.


#10

Just do something like it:

def join("rooms:" <> room_id, _payload, socket) do
  {:ok, assign(socket, :example, :sample)}
end

Look that for every called (i.e. not declared in code) join you have new process. If you are joining to for example RoomsChannel then you would have something like connection for many-to-many relation betweenRoom (existing lobby or new one) and User (assigned for example on Sockect.connect/2 or in same RoomsChannel.join/3 or simple guest if you support it).