Unknown registry GenServer

I think you would also be able to implement something simple with the PartitionSupervisor coming in 1.14. You would have to do some hashing to make sure the messages are not duped on multiple server partitions.

To me, node up and new user are separate topics, so by splitting them you can have other processes subscribe to one or the other in the future without coupling them together

I put the NodeListener as its own process precisely because it has one responsibility. It reduces the load on Repo because Repo doesn’t care about nodedown (at least that’s what I took away from what you said). And if other processes may be interested in cluster membership, the NodeListener can be the hub for those events for the whole node, without needing multiple processes to be subscribing to net_kernel.monitor_nodes.

If there is an error that you expect might happen and you have a way to recover from it, then I think you handle it explicitly with pattern matching. If there is a real exception with no recovery then “let it crash”, and the supervisor will restart your process at a known good state.

I added this because you said

So I assumed you were only interested in :nodeup, not :nodedown. If you monitor nodes you will receive both, so I discarded the :nodedown messages. Maybe that was a wrong assumption :smile:

Thanks for enlightening me. I will dig into the PartitionSupervisor. Many tools with OTP! It seems to make powerful and lightweight apps. It is surprising it’s not more widely used, for web apps I mean.

1 Like

Progressing slowly with Elixir. If I don’t use state in a module but only use the messaging part, I understand I may not need a Genserver. I used a supervised Task and use a receive do loop in the module. This seems to work (you can subscribe and receive the event/data between distributed nodes). However, this looks like I’m using Genserver with more complications and no benefit, aren’t I?

# some module broadcasts an event/payload: 
Phoenix.PubSub.broadcast(MyApp.PubSub, "node_up", {:new data})

# the "listener" module is started in the "children" array of the app supervisor
children = [..., {MyApp.Listener, opts},...]

defmodule MyApp.Listener
  # no need "child_spec"
  use Task, restart: : permanent

  def start_link(opts) do
    task = Task.start_link(__MODULE__, :start, [opts])
    IO.inspect(task, label: "start_link")
  end

  def start(opts) do
     do_something_with(opts)
      Phoenix.PubSub.subscribe(MyApp.PubSub, "node_up")
      monitor()
  end

  def monitor() do
    receive do
      {:new, payload} ->
        IO.inspect(payload, label: "Node UP -> action with: ")
        monitor()
    end
  end
end

Side note: I can’t reach for Process.whereis(MyApp.Listener) == nil, however the process appears by pid under the supervision tree.

My take on this comment that I think you’re referencing was that it’s guiding you to consider not using a GenServer for bare function calls when the GenServer’s state is being ignored. The original code you posted didn’t have any use for the GenServer state, nor was the GenServer receiving and responding to any pubsub messages, so it could have been written as a module and functions to allow parallel execution from any process, rather than serial execution from a single process.

Now that it’s clear you need a process to listen for and respond to changes in cluster membership, it is appropriate to use a GenServer for that. I think the way you’re using the Task as a replacement is a misunderstanding of the basis for that original comment, and a misuse of Tasks, which are really an abstraction for one-off computations. A GenServer is a general-purpose abstraction for listening to and handling messages in a loop, so it’s the right tool for the job IMO.

AFAIK you can’t register a name for a Task, precisely because they’re not meant to be used as this type of long running “reachable” process

Yes, when I read about the Task, it seemed like an inappropriate tool, that’s why I posted this. It was however instructive! Now it makes sense. Thanks.

1 Like