Miles of phoenix channels

Hi Everyone!
I have a app with, more or less, 5.000 users online that use websocket.

Each user connect to a private channel, like “user:1”, “user:2”

Each user can follow presence of another users on his screen, so he connect to a presence channel of those users, like “presence:user:1” (more or less 100 at time on screen)

Looking at :observer.start() I saw that each user open, at least, 100 processes. With 5k online users will be 500k processes.

I plan to run at AWS (C5.2xlarge - 8 vcpu / 16gb ram)…

Do you think that is ok? or it’ll be a issue?

When you talk about a presence channel is this a thing you’ve done yourself, or are you using Phoenix Presence?

Right now I use Elixir.Registry, but I can port to Phoenix.Tracker/Presence as well.

Another way, that I tryied to implement, was using only one channel per user, like “user:1” and
use socket message to track/untrack another users presences. So, i use same channel process on presence, use it to tracker users_ids and always broadcast to “user:1”

ex.:

defmodule Web.UserChannel do
  alias Web.{Presence}

  def join("user:" <> user_id_str, _payload, socket) do
    if to_string(socket.assigns.user_id) == user_id_str do
      send(self(), :after_join)
      {:ok, socket}
    else
      {:error, %{reason: "unauthorized"}}
    end
  end

  def handle_info(:after_join, socket) do
    user = socket.assigns.user
    Presence.track("presences", user.id)
    {:noreply, socket}
  end

  def handle_in("presence:track",%{"ids"=> ids}, socket) do
    for id <- ids, do: Presence.track("presence:user:#{id}", socket.assigns.user.id)
    {:noreply, socket}
  end

  def handle_in("presence:untrack",%{"ids" => ids}, socket) do
    for id <- ids, do: Presence.untrack("presence:user:#{id}")
    {:noreply, socket}
  end
end

Well, what I am saying is that you probably want to avoid doing “each user open, at least, 100 processes” just to track presence. Phoenix Presence will let you track and be notified about the presence of users without opening that many processes.

Yes,
But I need to front-end “tell” who he is seeing on screen… because each user has it how rules about who it can keep status (users can block each other, etc).

So, I think, best way is front-end subscribe to user presence using socket message and NOT a socket channel join…

Gotcha. Can you elaborate on your use case? I’m sure there’s a better solution than spawning a factorial number of processes.

Is something like whatsapp/facebook message… but use can have 10k of contacts.

  • User has a initial screen, load paged from database (not all 10k contacts) from a rest api.
  • User can see presence (online/offline) from those users
  • User should receive notifications about new messages, or when contact read his message
  • If user loose connect, presence and new messages has to be resync

Right new I use a rest api to load or send messages (controller insert into a database and broadcast to channel), and channel only to send updates from server.

Like a GraphQL subscription perhaps backed via a PostgreSQL LISTEN/NOTIFY? ^.^

But really, I’d use a single channel and just have it on the backend link to other topics as well and handle the messages back out.

1 Like