arnodirlam
Subscribe all clients/presences in one channel to an external topic
We have a use case where we want to route an incoming call to all online users within a channel (“lobby”). Each of these users (“candidate pool”) can either accept or decline the incoming call. The first user accepting the incoming call will be connected and the selection process ends (first come first serve, max. 1).
Before anyone accepted the call, each candidate still in the pool (= not declined yet) should know who else is still in the pool.
Since channels and Presence are made for that, my intuition is to implement the system using it.
My idea is to create an external topic (new channel) for the incoming call, then add all candidates (from the “lobby” channel) to it, and use broadcast Presence diffs whenever the pool’s state changes. When a user accepts the incoming call, the external channel is immediately closed.
However, I’d like to avoid broadcasting to all users in “lobby” to join the external channel (client-side roundtrip), because of performance, reliability and security. I’d like to keep the logic server-side, ideally preserving the multi-node attributes that channels+presence gives me out-of-the box.
If I understand correctly, this forbids approaches using the “lobby” subscribers’ PIDs directly.
So my approach would be to broadcast a message to “lobby” with the external channel’s topic, then intercept the broadcast, let handle_out have each subscriber join the new channel, and cancel the message going to the client. All server-side. Once all candidates have joined the external topic, standard channels+Presence broadcasting can handle the rest.
Is this the best approach for the given use case?
Am I overlooking something?
On a sidenote, the phoenix JS-client will not “know” it’s subscribed to the external channel, so I have to either use its onMessage callback, or manipulate the topic of each outgoing message server-side using intercept and handle_out, correct?
Thanks a lot in advance for any feedback! ![]()
Marked As Solved
OvermindDL1
Precisely! ![]()
Also indeed. ![]()
Presence is for presence information, not for broadcasting. If you want user channel to listen for specific things then have them join another specially named topic then broadcast to that. I do not mean have them join a new topic from the javascript side or even to spawn a new process, but literally just call MyServer.Endpoint.subscribe("new:topic:thing:o'whatever"), and when you want them to stop listening on that topic then just call MyServer.Endpoint.unsubscribe("new:topic:thing:o'whatever"). Anything broadcast to those new topics and not the ‘channel topic’ will not appear in handle_in/handle_out, but rather will appear in handle_info with a Phoenix.Socket.Broadcast structure, use it like this:
def handle_info(%Phoenix.Socket.Broadcast{event: "notifications:"<>_ = event, payload: %{notifications: notifications}, topic: _topic}, socket) do
offset = 0 #socket.assigns.offset
pageSize = length(notifications) #10
msg = %{
offset: offset,
pageSize: pageSize,
notifications: notifications,
}
push socket, event, msg
{:noreply, socket}
end
For example I use this in my notifications channel topic, then the notification internally registers to certain other things depending on which notification groups they are assigned to. ![]()
But yeah, presence is literally just for tracking presence in the group, not to communicating to it, for that you should subscribe to the new topic as well and broadcast to that. ![]()
Also Liked
OvermindDL1
Exactly on both. ![]()
Yep, handle_info just handles messages to this process that it does not know what they are, so that ‘you’ can handle them straight. ![]()
You might want to have a catch-all handle_info at the bottom of the handle_info’s too so a process does not crash if it happens to get a message it does not know (I log them).
No problem, I delved into the code for them when I started using them a while back, so it is good to spread the knowledge. ![]()
OvermindDL1
You can have more than one presence setup per channel as well, just have to give each a unique ID, but I use this quite often, just as a note. ![]()
But say you have N users in a lobby/pool, a call is made to this pool so a message is ‘broadcast’ to the topic id of the lobby/pool to pop up a message on the users interface or so. The first to accept it sends a message back to the call handler thing that has it join the call process while also sending a message to the other clients that it was accepted elsewhere with whatever other useful data. You may not even need presence for this unless you want to know who all is waiting rather than just broadcasting all willy-nilly (which is fine really ^.^). ![]()
But yeah, I’d have a user connect to a call when it is accepted, not when it is offered. You could even have them all join a presence so you know who is waiting for a response, and as they deny it they leave the presence, until one accepts then they all drop off when they get the message that it was accepted by someone else. ![]()
arnodirlam
Thanks for your fast reply and also commenting on the use case ![]()
You can have more than one presence setup per channel as well, just have to give each a unique ID, but I use this
quite often, just as a note.
Nice to know! I guess you pass in the original channel pid and another topic, similar to here?
You may not even need presence for this unless you want to know who all is waiting […] (which is fine really ^.^).
You might be right that the info is not needed. However, I think it would be really useful for the users to know, and I’m also eager to wrap my head around this use case of Phoenix, testing whether it could really work like I’ve described in my question, keeping all the multi-node niceties in place ![]()
You could even have them all join a presence so you know who is waiting for a response
This is exactly what I’m trying to achieve. Ideally with broadcasts to only those users in that second presence.
You’re talking about another Presence within the same channel, not a separate channel, right?
So I assume that
- I can just copy contents of one Presence to another, without having to deal with PIDs or anything internal, and it just works?
- All Presence diffs are sent to the whole channel, no matter if each subscriber has already declined, or even entered after the initial broadcast. Or can I broadcast to only those subscribers who are in the Presence?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









