0
votes

Elm, phoenix, and elixir are quite new to me so I thought I would make channels test app simple example app to test the use of phoenix channels. The app has other stuff in it too because its made from old "parts" but bear with me on this.

The idea is that you have several genservers making http calls to a phoenix endpoint. Basically they are just updating a list held in an agent process. That list is displayed in an Elm app through a phoenix channel. The goal was just to see what happens if the agent state is updated frequently with several processes.

So this is what I have so far. I have the phoenix site with the Elm app setup and a separate Elixir app with genservers making the updates. Everything works fine about 20 seconds but then the channel connection is cut and not reestablished unless I hit refresh on browser. I can see from logging that the backend is still working fine and there is no error on the browser console either. So whats the deal here? I thought that the channel connection should automatically reconnect if lost and why is it disconnecting anyway?

Im guessing that the problem is with the elm-phoenix-socket. Here is it is setup in the elm app:

socketServer : String
socketServer =
    "ws://localhost:4000/socket/websocket"

initPhxSocket : Phoenix.Socket.Socket Msg
initPhxSocket =
    Phoenix.Socket.init socketServer
        |> Phoenix.Socket.withDebug
        |> Phoenix.Socket.on "new:heartbeats" "heartbeats:lobby" ReceiveHeartbeats

Here is how the broadcast is done on the backend:

defmodule AbottiWeb.ApiController do
  use AbottiWeb.Web, :controller

  def index(conn, _params) do
    beats = AbottiWeb.HeartbeatAgent.get()
    json conn, beats
  end
  def heartbeat(conn, %{"agent" => agent} ) do
    AbottiWeb.HeartbeatAgent.update(agent)
    beats = AbottiWeb.HeartbeatAgent.get()
    AbottiWeb.Endpoint.broadcast("heartbeats:lobby", "new:heartbeats", beats)
    json conn, :ok
  end
end

so in essence the genservers are constantly making calls to that heartbeat endpoint. I doubt the problem is here though. Another possibility where the problem lies is the channel setup which looks like this:

user_socket.ex:

defmodule AbottiWeb.UserSocket do
  use Phoenix.Socket

  channel "heartbeats:*", AbottiWeb.HeartbeatChannel

  transport :websocket, Phoenix.Transports.WebSocket

  def connect(_params, socket) do
    {:ok, socket}
  end

  def id(_socket), do: nil
end

and heartbeat_channel.ex:

defmodule AbottiWeb.HeartbeatChannel do
  use AbottiWeb.Web, :channel
  require Logger
  def join("heartbeats:lobby", payload, socket) do
    Logger.debug "Hearbeats:lobby joined: #{inspect payload}"
    if authorized?(payload) do
      {:ok, socket}
    else
      {:error, %{reason: "unauthorized"}}
    end
  end

  # Channels can be used in a request/response fashion
  # by sending replies to requests from the client
  def handle_in("ping", payload, socket) do
    {:reply, {:ok, payload}, socket}
  end

  # It is also common to receive messages from the client and
  # broadcast to everyone in the current topic (heartbeats:lobby).
  def handle_in("shout", payload, socket) do
    broadcast socket, "shout", payload
    {:noreply, socket}
  end

  # This is invoked every time a notification is being broadcast
  # to the client. The default implementation is just to push it
  # downstream but one could filter or change the event.
  def handle_out(event, payload, socket) do
    Logger.debug "Broadcasting #{inspect event} #{inspect payload}"
    push socket, event, payload
    {:noreply, socket}
  end

  # Add authorization logic here as required.
  defp authorized?(_payload) do
    true
  end
end

So any ideas what the problem is? Im guessing it is something really simple.

Ok, I know now that the socket transport times out. But why does it do that?

1

1 Answers

2
votes

Well, I solved it with this:

  transport :websocket, Phoenix.Transports.WebSocket,
    timeout: :infinity

Don't know how harmful that is but this being a test app it doesn't really matter.