What's the idiomatic ways of storing/using the sname/module name for remote calls?

This might be a stupid question, but I don’t see it anywhere. My question is on the maintainability on deployment.

One service I have is a slack bot running in contanier (a simple module called SlackPost uses GenServer). The idea is that all other services talks to the bot to post to a slack channel. Currently I set RELEASE_NODE=slackbot@localhost and RELEASE_COOKIE=… before the app runs (app is built using mix release).

Now I am implementing another service B that runs in another container, which talks to slackbot@localhost, so I write this helper function in B:

def log(message) do
    GenServer.call({SlackPost, :slackbot@localhost}, {:service_logs, message})
end

The part I am worried is the plain, hardcoded module name SlackPost, and node name :slackbot@localhost:

  1. The atom SlackPost comes from nowhere in the project B, not even in the dependency list. A refactoring of the module name in source will break the log function.
  2. The sname :slackbot@localhost belongs to configuration but got hardcoded at call site. Changes in the RELEASE_NODE of slackbot breaks my code.

What’s the idiomatic way to talk to a remote node in a maintainable way? At least I want to have a source of truth for the node name part; changing slackbot@localhost to SlackbotAlice@localhost needs to change only one place for deployment and requires no changes at all for other services.

One way I can think of is to write helper functions in SlackPost, and import SlackPost in other services, instead of calling GenServer.call(...), call SlackPost.service_logs_remote(...).

defmodule SlackPost do
  def service_logs(message) do
    GenServer.call(__MODULE__, {:service_logs, message})
  end
  def service_logs_remote(message) do
    GenServer.call({__MODULE__, get_sname()}, {:service_logs, message})
  end
end

Thanks!

global is your friend.

Thanks. Yes, I actually used to use global. Connect me if I am wrong: SlackPost was registered as {:global, SlackPost} in start_link, and service B log was

GenServer.call({:global, SlackPost}, {:service_logs, message})
  1. It solves service discovery (pid discovery), so SlackPost.service_logs can be defined as GenServer.call({:global, __MODULE__},... and then can be imported by other modules. Problem 1 is solved.
  2. Still the node discovery part is unclear. I still need to call Node.connect :slackbot@localhost during service B initialization. It actually seems not solving the second problem. Pointers for node discovery? libcluster?

Yea libcluster is probably your best bet for that

1 Like

The configuration of the cluster isn’t normally the concern of an individual process or application, it’s a system-wide thing. Service B’s only concern is that something replies to messages sent to {:global, SlackPost}

1 Like