Is it possible to get all connected sockets on phoenix?

I want to know socket connected user list on phoenix.
I used Presence and it works but it notifies all connection changes to all clients. I don’t want it.
The idea I have now is monitoring sockets with worker, but I want to know any better idea.
Thanks!

Track all the users in Presence under a key different to the channel name. This will prevent users from receiving the diff messages.

4 Likes

cool! thx!

1 Like

I had another method found earlier within this forum and that is to
intercept(["presence_diff"]) inside the channel module. That’s the message phoenix tries to send to provide the presence to connected clients. By intercepting you are able the change the outgoing message (even changing it to a no-op and effectively not sending a message to the clients).
To change the presence_diff message you use the following in the same channel module (where you just added the intercept line from above):

def handle_out("presence_diff", _, socket), do: {:noreply, socket}

Because this function returns a noreply it will not send out any presence information to the connected clients.
I guess both methods work but maybe this method conveys your intention a little better.

1 Like

It surely does convey the intention quite well, but not trying to send diffs in the first place is probably the better implementation than trying to send them just to discard them on their way. I’m not sure how performance intensive a noop intercept is, but it has a warning that customizing messages per user can become a performance issue.

If you track users in presence using e.g. something like an internal:users topic I feel like the intend can be communicated in a similarly explicit fashion.

2 Likes

Maybe I’m doing something wrong but no matter the key I use (e.g.: internal:users), Presence still sends the presence_diff message to all users.

  def handle_info(:after_join, socket) do
    {:ok, _ref} =
      Presence.track(socket, "internal:users", %{
        user_id: socket.assigns.user_id,
        online_at: inspect(System.system_time(:second))
      })
    {:noreply, socket}
  end

This is Phoenix 1.6.10.

Are you sure the topic chosen is not one covered by a channel?

I would say so. This is the rest of my user_channel.ex file:

  def join("all_users", _params, socket) do
    user_id = socket.assigns.user_id
    {:ok, %{user_id: user_id}, socket}
  end

  def join("user:" <> user_id, _params, socket) when user_id == socket.assigns.user_id do
    send(self(), :after_join)
    {:ok, %{user_id: user_id}, socket}
  end

  def join("user:" <> other_user_id, _params, socket) do
    {:error, %{reason: "User must join his own channel"}}
  end

You’re not changing the topic here. When you use track/3 it’s track(socket, key, metadata). You want track/4 as track(pid, topic, key, metadata).

That was it. I was taking the term key in your comments literally, when I should have thought about it as the topic.

Thanks a lot @LostKobrakai. It is working now.

I’ve edited my post, so hopefully you’re the last one to fall for that.

1 Like