Skip to content

Instantly share code, notes, and snippets.

@ahammel
Last active August 29, 2015 13:57
Show Gist options
  • Select an option

  • Save ahammel/9697575 to your computer and use it in GitHub Desktop.

Select an option

Save ahammel/9697575 to your computer and use it in GitHub Desktop.
The foo system consists of a master process, a supervisor, and some worker
processes. The worker processes do a computationally expensive thing and send
the results to the master process, which then maybe asks the supervisor to
spawn more workers, depending on the result:
################ asks for workers... ####################
# foo_master # ------------------------> # foo_supervisor # --\
################ #################### |
^ | spawns....
| |
| ################ |
\--------------------------------- # foo_worker # <-----/
sends results... ################
I'm trying to figure out the nicest way to encode the message passing/spawning
loop without making a mess of the dependencies graph or resorting to naked
message passing. An API module for the entire foo system could do that, but it
seems like that module might be a bit awkward and 'know too much' about the
entire system, so to speak. If I wanted to change foo_master from a gen_server
to a gen_fsm, for instance, the foo_api module would probably have to change
as well.
-module(foo_master).
-behaviour(gen_server).
-export([start_link/1, init/1, handle_call/3]).
start_link(SupervisorPid) ->
gen_server:start_link(?MODULE, [SupervisorPid], []).
init([SupervisorPid]) ->
State = {some_stuff, SupervisorPid},
{ok, State}.
handle_call(Message, _From, State) ->
maybe_submit_more_jobs(Message, State),
{noreply, State}.
maybe_submit_more_jobs(Message, {SomeData, SupervisorPid}) ->
case some_complicated_condition(Message, SomedData) of
true -> submit(more_jobs, SupervisorPid);
false -> die(horribly)
end.
%%% ============================================================ %%%
submit(Jobs, SupervisorPid) ->
supervisor:start_child(SupervisorPid, [some_data, self()]). % Evil?
foo_worker_api:start(SupervisorPid, [some_data, self()]). % Circular dependency?
foo_api:start_worker(SupervisorPid, [some_data, self()]). % Seems awkward?
-module(foo_supervisor).
-behaviour(supervisor).
% Supervisor callbacks
-export([start_link/1, init/1]).
start_link([]) ->
supervisor:start_link(?MODULE, []).
init([]) ->
MaxRestart = 1,
MaxTime = 3600,
{ok, {{simple_one_for_one, MaxRestart, MaxTime},
[{sar,
{sar, start_link, []},
transient,
brutal_kill,
worker,
[sar]}]}}.
-module(foo_worker).
-behaviour(gen_server).
% Gen_server callbacks
-export([start_link/2, init/1,
handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link(SomeData, MasterPid) ->
gen_server:start_link(?MODULE, {SomeData, MasterPid}, []).
init({SomeData, MasterPid}) ->
gen_server:cast(?MODULE, do_some_analysis),
State = {SomeData, MasterPid},
{ok, State}.
handle_cast(do_some_analysis, {SomeData, MasterPid}) ->
Result = analyse(SomeData),
send(Result, MasterPid),
{stop, normal, State}.
%%% ============================================================ %%%
send(Result, MasterPid) ->
gen_server:call(MasterPid, Result). % Evil?
foo_master_api:send(MasterPid, Result). % Circular dependency?
foo_api:send_to_master(MasterPid, Result). % Seems awkward?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment