0

I'm trying to create a Phoenix Channel that just contains a clock that users can subscribe to.

defmodule MyWeb.TimeChannel do
  use Phoenix.Channel
  def join("room:time", _message, socket) do
    schedule_heartbeat()
    {:ok, socket}
  end

  def handle_info(_, socket) do
    broadcast!(socket, "update", %{time: DateTime.utc_now()})
    schedule_heartbeat()
    {:noreply, socket} 
  end

  @heartbeat_ms 1000
  defp schedule_heartbeat, do: Process.send_after(self(), :heartbeat, @heartbeat_ms)

end

The issue I have is when more than one client joins, I end up scheduling multiple ticks per second.

How can I just schedule one ticking clock?

cjm2671
  • 18,348
  • 31
  • 102
  • 161

1 Answers1

2

I fixed this by creating a separate GenServer that posts to the channel:

defmodule MarsWeb.TimeServer do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, %{}, name: :time_server )
  end

  def child_spec(arg) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [arg]},
      restart: :permanent,
    }
  end

  @impl true
  def init(_) do
    schedule_tick()
    {:ok, %{}}
  end

  @impl true
  def handle_info(:tick, state) do
    MarsWeb.Endpoint.broadcast!("room:time", "update", %{time: DateTime.utc_now()})
    schedule_tick()
    {:noreply, state} 
  end

  @tick_ms 1000
  defp schedule_tick, do: Process.send_after(self(), :tick, @tick_ms)

end
cjm2671
  • 18,348
  • 31
  • 102
  • 161
  • This is absolutely redundant, see my answer. – Aleksei Matiushkin Apr 22 '20 at 08:24
  • Ouch. Disregard my answer. There is no way even [to get to name/pid of the channel](https://github.com/phoenixframework/phoenix/blob/v1.4.16/lib/phoenix/channel.ex#L383-L407), so this solution is probably the best one. Sorry for the noise. – Aleksei Matiushkin Apr 22 '20 at 08:39
  • 1
    That's interesting to know. Thanks so much for your help! You've helped me a lot over the last year, so really appreciate it! Thank you! :) – cjm2671 Apr 22 '20 at 08:47