Phoenix Channels: Track the number of connected users per topic per host

I am wanting to build a dashboard that shows the number of users connected to each topic per node so I can visualise the distribution of users across servers.

I was hoping this information was already exposed somewhere in Phoenix but for the life of me I couldn’t figure it out so I wrote a small library to do it - https://github.com/coop/phoenix_channel_topic_counter.

Earlier in the year I queried slack but no one had a good solution and I received a few DMs asking for my solution.

If there is a different / better solution available I’d love to hear about it.

Thanks.

1 Like

The Phoenix built-in telemetry feature is your friend. See https://hexdocs.pm/phoenix/telemetry.html

I am actually working on some similar stuff in the PromEx Project https://github.com/akoutmos/prom_ex

At the moment I am using the [:phoenix, :channel_joined] telemetry event to capture metrics on how many channel joins have occurred but that won’t tell you how many people are currently attached to a channel as I don’t keep track of when people leave the channel. As for the channel that the joined event is in regards to, that is in the event metadata under the Phoenix.Socket struct:

event_metadata = %{
  params: %{},
  result: :ok,
  socket: %Phoenix.Socket{
    assigns: %{},
    channel: Phoenix.LiveReloader.Channel,
    channel_pid: #PID<0.633.0>,
    endpoint: WebAppWeb.Endpoint,
    handler: Phoenix.LiveReloader.Socket,
    id: nil,
    join_ref: "6",
    joined: false,
    private: %{log_handle_in: :debug, log_join: :info},
    pubsub_server: WebApp.PubSub,
    ref: nil,
    serializer: Phoenix.Socket.V2.JSONSerializer,
    topic: "phoenix:live_reload",
    transport: :websocket,
    transport_pid: #PID<0.630.0>
  }
}

Hope that helps!

1 Like

Telemetry is great as mentioned above. However, I’m not sure that there is a telemetry event for when a channel is left, which is a problem for your app. Also, you will be getting channel level metrics rather than socket level (you could work around this though).

I have done this manually in the past with a linked GenServer (sort of how Tracker does). It keeps track of all Socket joins and Channel joins for the current node (no distribution). You can find the code here. I used a hack in order to track it for a Socket, although I would now rewrite that to manually spec defoverridable instead of redefining the code.

If you want to track Channel joins only, then it’s easier because you can hook into the join function like you would normally do with Phoenix.Tracker.

I was hoping there were telemetry events for this kind of stuff but like @sb8244 mentioned, there aren’t any leave events.

My implementation uses a linked gen server for tracking - I think I looked at the tracker code initially and build a solution similar but specific to my use case.

I don’t know what @akoutmos is suggesting for tracking leaves but I’m guessing I could hook the channel_joined and monitor / link the socket.channel_pid. I’d still need 99% of my code but I could remove the explicit call to inc/1 in favour of pulling it from the event metadata.

At least it doesn’t feel like there was an obvious solution I overlooked.

Someone can correct me here, but I imagine the issue with tracking leaves is that the disconnection event can’t be guaranteed depending on how the process is killed. That may be wrong depending on supervision setup, but my immediate thought goes to some edge cases that prevent complete guarantees.

I assume you’re correct if you are wanting to track disconnects from within the process that is “dying”. That’s why I am monitoring the channel process separately.

1 Like