saleh-tekm

saleh-tekm

Is inter-project supervision possible?

Hi everyone,

I’m relatively new to Elixir/Erlang, and I’m working on a demo project. The setup involves a master supervisor, a supervisor, and a worker, and I need to run each of them on separate devices. I’m unsure how these components will communicate when they’re on different machines.

To tackle this, I created separate projects for each component and connected them using Node.connect() and Phoenix.PubSub. However, I realized that this approach doesn’t handle supervision properly, right?

I feel like I might be lacking in the fundamentals of Elixir/Erlang, and I’d really appreciate some guidance on how to implement this correctly.

Thanks in advance! :smiley:

Marked As Solved

asabil

asabil

This is of course possible, here is a crude example in Erlang:

-module(dsup).
-export([
    main/0
]).


-export([
    child_init/1,
    child_loop/1
]).

-behaviour(supervisor).
-export([
    init/1
]).


% Demo entry point
main() ->
    {ok, _, Node} = peer:start_link(),
    supervisor:start_link({local, ?MODULE}, ?MODULE, [Node]).

% Child process init
child_init(Node) ->
    Parent = self(),
    {ok, proc_lib:spawn_link(Node, ?MODULE, child_loop, [Parent])}.


% Child process main loop
child_loop(Parent) ->
    receive
        {echo, From, Message} ->
            From ! {echoed, Message},
            child_loop(Parent);
        {stop, From, Reason} ->
            From ! ok,
            exit(Reason)
    end.


% Supervisor init callback
init([Node]) ->
    Flags = #{},
    Children = [
        #{
            id => child,
            start => {?MODULE, child_init, [Node]}
        }
    ],
    {ok, {Flags, Children}}.

The only thing a supervisor care about is a that a child start function starts a new process, liinks it to the supervisor and return the {ok, Pid} (or {ok, Pid, Info}), the Pid can be started on a remote node as the example above show.

Here is an example session:

> erl -sname node1
Erlang/OTP 27 [erts-15.1.2] [source] [64-bit] [smp:14:14] [ds:14:14:10] [async-threads:1] [jit] [dtrace]

Eshell V15.1.2 (press Ctrl+G to abort, type help(). for help)
(node1@Delta-23)1> c(dsup).
{ok,dsup}
(node1@Delta-23)2> dsup:test().
{ok,<0.101.0>}
(node1@Delta-23)3> supervisor:which_children(dsup).
[{child,<15315.91.0>,worker,[dsup]}]
(node1@Delta-23)4> ChildPid = pid(15315,91,0), ChildPid ! {echo, self(), "hello"}.
{echo,<0.90.0>,"hello"}
(node1@Delta-23)5> flush().
Shell got {echoed,"hello"}
ok
(node1@Delta-23)6> ChildPid ! {stop, self(), crashed}.
{stop,<0.90.0>,crashed}
=SUPERVISOR REPORT==== 19-Dec-2024::18:59:02.505399 ===
    supervisor: {local,dsup}
    errorContext: child_terminated
    reason: crashed
    offender: [{pid,<15315.91.0>},
               {id,child},
               {mfargs,{dsup,child_init,['peer-2242-11228@Delta-23']}},
               {restart_type,permanent},
               {significant,false},
               {shutdown,5000},
               {child_type,worker}]

=CRASH REPORT==== 19-Dec-2024::18:59:02.505058 ===
  crasher:
    initial call: dsup:child_loop/1
    pid: <15315.91.0>
    registered_name: []
    exception exit: crashed
      in function  dsup:child_loop/1 (dsup.erl, line 36)
    ancestors: [dsup,<0.90.0>,<0.89.0>,<0.76.0>,<0.71.0>,<0.75.0>,<0.70.0>,
                  kernel_sup,<0.47.0>]
    message_queue_len: 2
    messages: [{exit,crashed},{exit,<0.90.0>,crashed}]
    links: [<0.101.0>]
    dictionary: []
    trap_exit: false
    status: running
    heap_size: 376
    stack_size: 29
    reductions: 168
  neighbours:

(node1@Delta-23)7> supervisor:which_children(dsup).
[{child,<15315.92.0>,worker,[dsup]}]

As you can see in the last line, the supervisor restarted the process on the remote node.

That being said, you might probably want to take a look at the distributed applications guide for your use case (Distributed Applications — Erlang System Documentation v29.0.2)

Hope this helps.

Also Liked

LostKobrakai

LostKobrakai

While process links can span across nodes usually supervision and supervision trees are a thing local to a single node.

sbuttgereit

sbuttgereit

My sense is that a distributed application that is fault tolerant is a different thing from a distributed fault tolerance mechanism. A distributed application that is fault tolerant seems a reasonable and achievable goal (with some limitation), but a distributed fault tolerance mechanism I feel like could never be better than “fragile” or “unreliable” across too many scenarios.

I can see maybe having a specific, maybe more robust, device taking a role as some sort of controller node for commanding distributed processes to start, shutdown (gracefully) as needed, or just to monitor status… but not in the sense of supervision trees, but in the sense that the child processes can receive messages from the central node and that send responses as needed.

For fault tolerance and supervision… I’d be inclined to keep all of that local to the node. To my knowledge it solves a lot of questions distributed supervision might leave you with. What happens if the communication (network) between the child and supervisor isn’t working? Does the child have to detect this and die (self-supervision)? If the supervisor on node a doesn’t see the child in the right time (again, maybe network), does it try to restart the remote child and if the remote child isn’t actually dead is that OK?

I think if I’m really needing something like this, I’m going beyond just baseline OTP, am not really very familiar with OTP, the first thing I’m going to look for is a library (GitHub - elixir-horde/horde: Horde is a distributed Supervisor and Registry backed by Postgres · GitHub, maybe GitHub - bitwalker/libcluster: Automatic cluster formation/healing for Elixir applications · GitHub, etc.) built by others that are better versed in the issues. At least study them to get a handle on it.

I need to disclaim something here: I don’t have much hands-on experience with this in the Elixir/Erlang context so my thinking could well be flawed… but having worked with these kinds of distributed things before I have some instincts for it.

asabil

asabil

Does the child have to detect this and die (self-supervision)?

That’s what linking does, the linking is bidirectional, so if the link is severed between the processes, both processes receive an exit signal. What makes supervisors special is that they trap_exit. In this scenario it will mean that the child will terminate if the supervisor is no longer reachable.The supervisor will also receive an exit signal if the child is no longer reachable, but instead of terminating it will attempt to start a new child to replace the old one. Now if that fails many times, the supervisor itself will fail.

This of course doesn’t have to span networks. It could be 2 separate nodes on the same machine, or separate machines connected directly with an Ethernet/serial cable, it really all depends on the topology of the system.

All in all, the most important is to define/understand the failure modes and possibilities of your system. Is there a network? Is the network a possible problem? Is hardware failure a possibility you want to handle? Can someone yank a cable?

Once understood, you can build the right mechanism to handle the failures. No system can handle every single possible failure.

Where Next?

Popular in Questions Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&amp;query=perfume&amp;page=2, I would like to get: ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; somethi...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Other popular topics Top

New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39297 209
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

We're in Beta

About us Mission Statement