I've an umbrella project I'm working on to get up-to speed with non-trivial Erlang OTP concepts.
Maybe I'm committing wrong think. But I'd like to have various distinct features as their own little applications, with their own supervisor stuff, under the apps/
sub-directory, and a main entry point that glues various bits (PIDs) together were necessary.
Ideally each apps/<name>/src/<name>.app.src
file would define defaults, which may or may not be overwritten when the main entry-point is launching. At this point I'd be pretty happy with apps/<name>/src/<name>.app.src
being the single source of truth!
TLDR questions
- How do I ensure proper supervision of
node_to_watch
within thesup_my_apps
and correctly load<name>.app.src
configuration files for each? - Or said another way; how do I make
sup_my_apps
supervise thenode_to_watch
while making each app underapps/
sub-directory loads their respective<NAME>.app.src
configuration file?
TLDR of things I've tried
- define
ChildSpecs
insup_my_apps_sup:init/1
with MFA stuff for thenode_to_watch
up-start, which at time of writing, preventsapps/node_to_watch/src/node_to_watch.app.src
file from being considered. - define
node_to_watch
inapplications
within theapps/sup_my_apps/src/sup_my_apps.app.src
file, this does loadapps/node_to_watch/src/node_to_watch.app.src
but nowsup_my_apps_sup
ain't supervising it, so ain't gonna restart it after crashes.
Following sections details the paths of partial to complete failure I've already trodden trying to solve this on my own within a MVP to express intent.
Project initialization
cd /tmp
rebar3 new umbrella sup_my_apps
cd "${_}"
pushd apps
rebar3 new app node_to_watch
popd
pwd
#> /tmp/sup_my_apps
Setup sup_my_apps
Define explicit MFA
stuff for umbrella's main entry-point;
--- a/apps/sup_my_apps/src/sup_my_apps.app.src
+++ b/apps/sup_my_apps/src/sup_my_apps.app.src
{registered, []},
- {mod, {sup_my_apps_app, []}},
+ {mod, {sup_my_apps_app, [start, {normal, "Loaded args from sup_my_apps.app.src"}]}},
{applications, [
Add some debug-printing to the main entry point;
--- a/apps/sup_my_apps/src/sup_my_apps_app.src
+++ b/apps/sup_my_apps/src/sup_my_apps_app.src
-start(_StartType, _StartArgs) ->
+start(StartType, StartArgs) ->
+ io:format("#> ~p:start(~p, ~p)~n", [?MODULE, StartType, StartArgs]),
sup_my_apps_sup:start_link().
Check things are injected okay so far;
rebar3 shell
# ...
#> sup_my_apps_app:start(normal, [start,
#.. {normal,
#.. "Loaded args from sup_my_apps.app.src"}])
# ...
... LGTM!
Full state of apps/sup_my_apps/src/sup_my_apps_app.src
-module(sup_my_apps_app).
-behaviour(application).
-export([start/2, stop/1]).
start(StartType, StartArgs) ->
io:format("#> ~p:start(~p, ~p)~n", [?MODULE, StartType, StartArgs]),
sup_my_apps_sup:start_link().
stop(_State) ->
ok.
Full state of apps/sup_my_apps/src/sup_my_apps.app.src
{application, sup_my_apps, [
{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {sup_my_apps_app, [start, {normal, "Loaded args from sup_my_apps.app.src"}]}},
{applications, [
kernel,
stdlib
]},
{env, []},
{modules, []},
{licenses, ["Apache-2.0"]},
{links, []}
]}.
Take a breath
Above feels okay!
Setup apps/node_to_watch
Define explicit MFA
stuff for umbrella's node/child app;
--- a/apps/node_to_watch/src/node_to_watch.app.src
+++ b/apps/node_to_watch/src/node_to_watch.app.src
{registered, []},
- {mod, {node_to_watch_app, []}},
+ {mod, {node_to_watch_app, [start, {normal, "Loaded args from node_to_watch.app.src"}]}},
{applications, [
Add some debug-printing to the node;
--- a/apps/node_to_watch/src/node_to_watch_app.src
+++ b/apps/node_to_watch/src/node_to_watch_app.src
-start(_StartType, _StartArgs) ->
+start(StartType, StartArgs) ->
+ io:format("#> ~p:start(~p, ~p)~n", [?MODULE, StartType, StartArgs]),
node_to_watch_sup:start_link().
Check things are injected okay so far;
rebar3 shell
# ...
#> sup_my_apps_app:start(normal, [start,
#.. {normal,
#.. "Loaded args from sup_my_apps.app.src"}])
# ...
... hmm, nope no joy just yet!
Full state of apps/node_to_watch/src/node_to_watch.app.src
{application, node_to_watch, [
{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {node_to_watch_app, [start, {normal, "Loaded args from node_to_watch.app.src"}]}},
{applications, [
kernel,
stdlib
]},
{env, []},
{modules, []},
{licenses, ["Apache-2.0"]},
{links, []}
]}.
Full state of apps/node_to_watch/src/node_to_watch_app.src
-module(node_to_watch_app).
-behaviour(application).
-export([start/2, stop/1]).
start(StartType, StartArgs) ->
io:format("#> ~p:start(~p, ~p)~n", [?MODULE, StartType, StartArgs]),
node_to_watch_sup:start_link().
stop(_State) ->
ok.
Begin questioning life choices
Below is where things feels not okay...
Try define ChildSpecs
in sup_my_apps_sup:init/1
Okay maybe we gots to add the node to the ChildSpecs
list in sup_my_apps_sup:inet/1
--- a/apps/sup_my_apps/src/sup_my_apps_sup.src
+++ b/apps/sup_my_apps/src/sup_my_apps_sup.src
init([]) ->
SupFlags = #{
strategy => one_for_all,
intensity => 0,
period => 1
},
- ChildSpecs = [],
+ ChildSpecs = [
+ #{
+ id => node_to_watch,
+ start => {node_to_watch_app, start, [normal, "called by sup_my_apps_sup:init"]},
+ type => supervisor
+ }
+ ],
{ok, {SupFlags, ChildSpecs}}.
Check things are injected okay so far;
rebar3 shell
# ...
#> sup_my_apps_app:start(normal, [start,
#.. {normal,
#.. "Loaded args from sup_my_apps.app.src"}])
#> node_to_watch_app:start(normal, "called by sup_my_apps_sup:init")
# ...
... Well, it did successfully start the node_to_watch
app, and its supervisor, but doing it this way fails to load the node_to_watch.app.src
file!
Full state of apps/sup_my_apps/src/sup_my_apps_sup.src
-module(sup_my_apps_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
SupFlags = #{
strategy => one_for_all,
intensity => 0,
period => 1
},
ChildSpecs = [
#{
id => node_to_watch,
start => {node_to_watch_app, start, [normal, "called by sup_my_apps_sup:init"]},
type => supervisor
}
],
{ok, {SupFlags, ChildSpecs}}.
Rage against the machine
Below feels questionable too...
Back-peddle a bit and try updating main entry-point's configs
Revert ChildSpecs
list changes in sup_my_apps_sup:inet/1
--- a/apps/sup_my_apps/src/sup_my_apps_sup.src
+++ b/apps/sup_my_apps/src/sup_my_apps_sup.src
init([]) ->
SupFlags = #{
strategy => one_for_all,
intensity => 0,
period => 1
},
+ ChildSpecs = [],
- ChildSpecs = [
- #{
- id => node_to_watch,
- start => {node_to_watch_app, start, [normal, "called by sup_my_apps_sup:init"]},
- type => supervisor
- }
- ],
{ok, {SupFlags, ChildSpecs}}.
Instead add the node_to_watch
to sup_my_apps.app.src
file
--- a/apps/sup_my_apps/src/sup_my_apps.app.src
+++ b/apps/sup_my_apps/src/sup_my_apps.app.src
{applications, [
kernel,
- stdlib
+ stdlib,
+ node_to_watch
]},
Check things are injected okay so far;
rebar3 shell
# ...
#> node_to_watch_app:start(normal, [start,
#.. {normal,
#.. "Loaded args from node_to_watch_app.app.src"}])
#> sup_my_apps_app:start(normal, [start,
#.. {normal,
#.. "Loaded args from sup_my_apps.app.src"}])
# ...
... Oh-ho... We've got signs of configurations being loaded, woot!
But now how do I make sup_my_apps
supervise the node_to_watch
while making each app under apps/
sub-directory loads their respective <NAME>.app.src
configuration file?
Full state of apps/sup_my_apps/src/sup_my_apps_sup.src
-module(sup_my_apps_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
SupFlags = #{
strategy => one_for_all,
intensity => 0,
period => 1
},
ChildSpecs = [],
{ok, {SupFlags, ChildSpecs}}.
Full state of apps/sup_my_apps/src/sup_my_apps.app.src
{application, sup_my_apps, [
{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {sup_my_apps_app, [start, {normal, "Loaded args from sup_my_apps.app.src"}]}},
{applications, [
kernel,
stdlib,
node_to_watch
]},
{env, []},
{modules, []},
{licenses, ["Apache-2.0"]},
{links, []}
]}.