I am writing a simple Erlang application that manages auctions. When a new auction is created I would like to create a new child process to handle it. The function call is the following one:
erws_dynamic_sup:start_auction_process(PhoneName, MinimumPrice, AuctionTime, EndDate);
Since I want to monitor them, I am trying to use a simple_one_for_one supervisor, that should add them dynamically calling start_child/2.
This is the code of the supervisor (erws_dynamic_sup):
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
MaxRestarts = 10,
MaxSecondsBetweenRestarts = 3600,
SupFlags = #{strategy => simple_one_for_one,
intensity => MaxRestarts,
period => MaxSecondsBetweenRestarts},
ChildSpecs = [
#{
id => erws_auction_handler,
start => {erws_auction_handler, auction_handle, []},
restart => transient,
shutdown => 2000,
type => worker,
modules => [erws_auction_handler]
}
],
{ok, {SupFlags, ChildSpecs}}.
%%% Function to start an auction process
start_auction_process(PhoneName, MinimumPrice, AuctionTime, EndDate) ->
case supervisor:start_child(?MODULE,
[PhoneName, MinimumPrice, AuctionTime, EndDate]
) of
{ok, AuctionPid} ->
logger:info("[erws_dynamic_sup] start_auction_process => Start child executed correctly, PID: ~p~n", [AuctionPid]),
AuctionPid;
Error ->
logger:info("[erws_dynamic_sup] start_auction_process => Error: ~p~n", [Error]),
Error
end.
The function that manages the auction process, that should be started by the supervisor, is the following one (module erws_auction_handler):
auction_handle(Phone, Bid, AuctionTime, EndDate) ->
logger:debug("[erws_auction_handler] auction_handle => Auction process started with pid: ~p~n", [self()]),
erws_mnesia:save_auction(Phone, self()),
Text = "Live auctions update requested!",
gproc:send({p, l, {erws_auction_agent, {live_auctions}}}, {live_auctions_update, Text}),
auction_receive(Phone, Bid, AuctionTime, EndDate).
%% Handle auction messages
auction_receive(Phone, Bid, AuctionTime, EndDate) ->
receive
%% Receive JOIN request from a Bidder
{bidder_join, PhoneName} ->
RemainingTime = get_time_remaining(EndDate),
CurrentWinner = erws_mnesia:get_winner_bidder(PhoneName),
case CurrentWinner of
{WinnerEmail, _} ->
gproc:send({p, l, {?MODULE, PhoneName}}, {joined, Bid, RemainingTime, WinnerEmail});
not_found ->
gproc:send({p, l, {?MODULE, PhoneName}}, {joined, Bid, RemainingTime, []})
end,
auction_handle(Phone, Bid, EndDate - erlang:system_time(second), EndDate);
... other cases
end.
The problem I have is that this works fine only when I have a single auction. When I start multiple auctions at the same time, the first child process will go on with its execution, while the second one will stop and wait for the other to finish before proceeding with its execution (in this case saving the PID on mnesia). The code does not stop in the saving part, because not even the first log print of the auction_handle function is shown:
(logger:debug("[erws_auction_handler] auction_handle => Auction process started
).
What could the problem be? I thought of mapping the PID of the auction processes using gproc as I did to subscribe users to notifications about the price for example, but I don't know if this could solve the problem since the process doesn't start at all its execution.
The problem is that the start
function in the supervisor child spec is supposed to spawn a new process and return {ok, Pid}
- the supervisor
module doesn't spawn the process for you. It just waits for the start function to return.
So change the child spec to:
#{
id => erws_auction_handler,
start => {erws_auction_handler, start_link, []},
And add the erws_auction_handler:start_link/4
function:
start_link(Phone, Bid, AuctionTime, EndDate) ->
Pid = spawn_link(?MODULE, auction_handle, [Phone, Bid, AuctionTime, EndDate]),
{ok, Pid}.
(don't forget to export start_link/4
!)
Another thing you could do is changing the erws_auction_handler
module to follow the gen_server behaviour. In that case, the erws_auction_handler:start_link
function would just call gen_server:start_link
, and incoming messages would be received through the handle_info
callback function instead of through a receive
expression.