How to create a process subscribed to a channel topic to watch the handle_diff events?

I have opened this issue in phoenix git and chrismccord answered me “If I understand you correctly, you are intercepting handle_diff, but not having it fire for the “last” client. This is expected because intercepts happen in the channel process. So by it leaving, it’s long dead and there won’t be a handle_diff to intercept. You need to have another process subscribe to the topic and watch the handle_diff events.” How can I do this another process subscript to this topic using phoenix? Is there a way of watch the socket instead of the channel to make this? Is there another way to know that the channel died? Can anyone help me?

Environment

Elixir version (elixir -v): 1.3.4
Phoenix version (mix deps): 1.2.1
NodeJS version (node -v): 4.2.6
NPM version (npm -v): 3.5.2
Operating system: Linux 16.04
Expected behavior

I have a raspberry pi that is the control unit of my system, which is a Python client on a websocket connection with Phoenix Server. The control unit connects to a socket (ControlUnitSocket) and then joins its channel. There is only one control_unit per channel and I need to know if this client has left the channel due to any problem (energy, network disconnection etc). I am using Phoenix.Presence to do that. So when the control unit leaves channel I try to intercept the “presence_diff” event to trigger some actions.

def handle_info(:after_join, socket) do
  push socket, "presence_state", Presence.list(socket)
  {:ok, _} = Presence.track(socket, socket.assigns.control_unit_id, %{
    online_at: inspect(System.system_time(:seconds))
  })
  {:noreply, socket}
end

intercept ["presence_diff"]

def handle_out("presence_diff", msg, socket) do
  url="save_history"
  IO.puts("Entrou no Presence DIFF")
  response = Appserver.post!(url, msg)
  IO.puts("REsposta do servidor foi:")
  IO.inspect(response.status_code)
  {:noreply, socket}
end

Actual behavior

But I never receive this “presence_diff” event. I only receive this “presence_diff” event if I have more than one control unities in the same channel, so when they disconnect I receive the “presence_diff” event, with the exception of the last one control unity that when it disconnects no event is received. What do I have to do to know if the only control unity connected and joined to a channel has disconnected? Is it right this behavior of Phoenix.Presence of not sending the “presence_diff” event when the last entity disconnect from channel?

2 Likes

It’s not entirely clear what your use case is, but you have a couple options:

  1. On join, have another process monitor you, then when you disconnect, they will receive {:DOWN, ...} and you can handle whatever you need to do. This only works assuming you need to react to clients leaving the same way, regardless of how many are connected. For example, if I open 5 tabs, this setup will fire the callbacks as I close each one, rather than when my “last” tab is closed. This may or may not be what you’re wanting.

  2. You can have another process call MyApp.Endpoint.subscribe("some:topic") where the topic is the channel topic(s) you’re wanting to watch over. This process will then receive presence_diff events and you can react accordingly. This setup let’s you know more, like how many tabs/devices are connected for the same user, and whether the “last” one has left, but then you also need to manage when to subscribe to new topics, and unsubscribe from ones that are no longer being tracked by active channels on the local node.

3 Likes

Could you help me how to implement this second case? I am a little confused about how doing that.

I also needed to deal with such a use case. For anybody stumbling upon this question, basically one just needs to define a GenServer and start the process under the children list in my_app.ex. chrismccord actually provided a quite detailed code example here: https://stackoverflow.com/questions/33934029/how-to-detect-if-a-user-left-a-phoenix-channel-due-to-a-network-disconnect Just call MyApp.Endpoint.subscribe("some:topic") in that process instead of what the original answer did.

3 Likes

and unsubscribe from ones that are no longer being tracked by active channels on the local node.

If I don’t unsubscribe, will that cause a memory leak (or a resource leak of some other sort)?