最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Rebar3 Umbrella Erlang load `.app.src` in multi-app multi-supervisor project - Stack Overflow

programmeradmin4浏览0评论

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 the sup_my_apps and correctly load <name>.app.src configuration files for each?
  • Or said another way; 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?

TLDR of things I've tried

  • define ChildSpecs in sup_my_apps_sup:init/1 with MFA stuff for the node_to_watch up-start, which at time of writing, prevents apps/node_to_watch/src/node_to_watch.app.src file from being considered.
  • define node_to_watch in applications within the apps/sup_my_apps/src/sup_my_apps.app.src file, this does load apps/node_to_watch/src/node_to_watch.app.src but now sup_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, []}
 ]}.
发布评论

评论列表(0)

  1. 暂无评论