How can i send state to a process without hardcoding its pid or registered name?

I am having a GenServer running that updates its state after every five seconds. I need to send that state after every 5 seconds to another GenServer but I really don’t want to hard code the PID of other GenServer as the name might change in the future.

Is there any way that the receiver knows about the sender but the sender doesn’t know about the receiver?

My Code

defmodule Sender do
  use GenServer

  def start_link(process_name) do
    GenServer.start_link(__MODULE__, [[], process_name], name: process_name)
  end

  # server
  def init([state, process_name]) do
    schedule_state_send(process_name)
    {:ok, state}
  end

  def handle_info({:table_state, process_name}, state) do
    IO.puts("Current state of #{inspect(process_name)} is #{inspect(state)}")
    schedule_state_send(process_name)
    sending_state_to_view(process_name, state)
    state = []
    {:noreply, state}
  end

  def schedule_state_send(process_name) do
    Process.send_after(self(), {:table_state, process_name}, 5_000)
  end

  def sending_state_to_view(process_name, state) do
     # code goes here
  end
end

My Reciever GenServer
How can it receive the state that the sender has to send continuously.

defmodule Reciever do
  use GenServer

  # client
  def start_link(process_name) do
    GenServer.start_link(__MODULE__, [], name: process_name)
  end

  # server
  def init(view_state) do
    {:ok, view_state}
  end
end

There are few different approaches and the simplest one I can think of is to send PID from one process to another:

defmodule Sender do
  use GenServer

  def register(name), do: GenServer.call(name, {:register, self()})

  def start_link(process_name) do
    GenServer.start_link(__MODULE__, %{pid: nil}, name: process_name)
  end

  # server
  def init(state) do
    {:ok, state}
  end

  def handle_call({:register, pid}, _caller, _state) do
    {:reply, :ok, %{pid: pid}, {:continue, :schedule}}
  end

  def handle_info(:ping, %{pid: nil} = state), do: {:noreply, state}
  def handle_info(:ping, %{pid: pid} = state) when is_pid(pid) do
    send(pid, :ping)
    {:noreply, state, {:continue, schedule}}
  end

  def handle_continue(:schedule, state) do
    Process.send_after(self(), :ping, 5_000)
    {:noreply, state}
  end
end

And then

defmodule Receiver do
  use GenServer

  require Logger

  # client
  def start_link(sender) do
    GenServer.start_link(__MODULE__, sender)
  end

  # server
  def init(sender) do
    Sender.register(sender)
    {:ok, []}
  end

  def handle_info(:ping, state) do
    Logger.info("Received ping")
    {:noreply, state}
  end
end
1 Like

PubSub! Phoenix.PubSub is very good, and can be used entirely server-side … you can subscribe to topics and push events to channels entirely on the server side. I use this extensively to publish change notifications from domain modules that are picked up by GenServers and even LiveViews to change their assigns.

Related to this is the Registry module, which can allow you to register a process on an arbitrary name and look it up …

You can also look into process groups which allow 1 or more processes to share a name, and then you broadcast messages to all members of them, with the conceit that group names are less likely to change than individual names.

4 Likes

I will study pubsub, thanks alot

The sender can have multiple instances, means that there can be multiple senders each having their own process id. How will this code identify which process is sending information. I am not getting the code.

1 Like