I need to send a message to a globally registered process, that might be unavailable for a short-time, when it is replaced with a backup process (i.e. failover).
Is the following snippet good Erlang code:
% send message to globally registered process, with possibility to retry once
send_message(To, Message, Retry) ->
try global:send(To, Message)
catch
% registered process To is unavailable
exit: {badarg, {To, Message}} ->
io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
case Retry of
true ->
% retry sending message, after 1 second
sleep(1000),
send_message(To, Message, false);
false ->
% re-throw caught exit, including stack trace
erlang:raise(exit, {badarg, {To, Message}},
erlang:get_stacktrace())
end
end.
The Retry parameter is either true or false, indicating the message should be retried once if there was a problem. If the message can still not be sent, I want the same exception to be raised as would have been the case by calling global:send(To, Message) outside a try-catch block.
I know the above works, but I am concerned whether the false section of my case block is good erlang (e.g. using erlang:raise() and rlang:get_stacktrace()).
Any thoughts or suggestions to make the code "better"?
Make two different calls to global:send, one inside a try ... catch, the other not:
send_message(To, Message, 0, _, _) ->
global:send(To, Message);
send_message(To, Message, RetriesLeft, RetryDelayMs, MaxRetryDelayMs) ->
try
global:send(To, Message)
catch
% registered process To is unavailable
exit: {badarg, {To, Message}} ->
io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
% retry after RetryDelayMs milliseconds
sleep(min(RetryDelayMs, MaxRetryDelayMs)),
send_message(To, Message, RetriesLeft - 1, 2 * RetryDelayMs, MaxRetryDelayMs)
end.
EDIT: Added exponential back-off. Be like … nope, just can't do it.
I would do
erlang:error({badarg,{To,Message}})
instead. No real difference as this also generates a stack trace but I think it is clearer. erlang:raise/3 is better for a more generic usage and if you want do things with the stacktrace.
Related
I am trying to update my process's state on a 10 second timer.
-define(INTERVAL, 3000).
start_link() ->
gen_server:start_link(?MODULE, [], []).
action(Pid, Action) ->
gen_server:call(Pid, Action).
init([]) ->
erlang:send_after(?INTERVAL, self(), trigger),
{ok, temple:new()}.
what I want to do is call this
handle_call({fight}, _From, Temple) ->
NewTemple = temple:fight(Temple),
{reply, NewTemple, NewTemple};
So I try
handle_info(trigger, _State) ->
land:action(self(), {fight}),
erlang:send_after(?INTERVAL, self(), trigger);
but I get
=ERROR REPORT==== 4-Dec-2016::19:00:35 ===
** Generic server <0.400.0> terminating
** Last message in was trigger
** When Server state == {{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]}}},
[]}
** Reason for termination ==
** {function_clause,[{land,terminate,
[{timeout,{gen_server,call,[<0.400.0>,{fight}]}},
{{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
[]}],
[{file,"src/land.erl"},{line,47}]}
It appears that with land:action(self(), {fight}), you're attempting to make a call to the same gen_server in which you're currently handling the trigger message. Two important facts will explain why this can't work:
A call always waits for the result to be returned.
A gen_server is a process, and a process can handle only one message at a time.
In handling the trigger message, you're saying to call back to yourself and wait for yourself to process the {fight} message. Since you're in the middle of handling the trigger message, though, you'll never get to the {fight} message. You're effectively in a deadlock with yourself. That's why you're getting a timeout.
P.S. Posting an SSCCE is far more likely to get you good answers.
The error message means that there is no terminate clause in the land server module, you should have a warning when you compile the land server module.
The terminate clause is called because a timeout occurs during the gen_server call with the parameters (Pid = <0.400.0>, Message = {fight}), which was called on line land:action(self(), {fight}),. A call to a gen_server must be completed within a maximum time, by default 5000ms, You have to reduce the time spent in the fight action.
Note that it is not a good idea to increase the server timeout since a gen_server call is blocking: during the execution of a gen_server call no new message can be handled, and in your example , it is also blocking the execution of the handle_info(trigger, _State) code.
Last point, the clause handle_info(trigger, _State) should return a tuple of the form {noreply,NewState} while the last line erlang:send_after(?INTERVAL, self(), trigger); returns a timer reference, you have to modify this line.
An example of this would be:
myFunction()
receive
msg1 -> io:format("Message 1!~n"),
self() ! msg1,
myFunction();
msg2 -> io:format("Message 2!~n")
end.
I learnt to do my messages like msg1; but recently I made an error and compiled my code similar to msg2. When msg2 occurs, what happens afterwards? Does the process just sit at that same receive after msg2 and wait for other messages?
Why don't you try it yourself?
If you don't call the function after receiving msg2, then there is nothing more to execute and your process will quit.
Does the process just sit at that same receive after msg2 and wait for other messages?
No. You need to call the function again. The recursion creates a loop.
When msg2 occurs, what happens afterwards?
Message 2!\n is printed and myFunction/0's code is no longer executed, and if the current process has nothing more to do it exits.
if you don't call the function again at the end of a receive block, the process will die.(if you case, if you send msg2 to the process), but if you send other message(except for msg1 and msg2), the process will wait, and the messages are stored in the process's message queue.
-module(wy).
-compile(export_all).
myFunction() ->
receive
msg1 ->
io:format("Message 1!~n"),
self() ! msg1,
myFunction();
msg2 ->
io:format("Message 2!~n")
end.
parent() ->
Pid = spawn(fun myFunction/0),
register(myFunction, Pid),
erlang:monitor(process, Pid),
receive
Res -> io:format("Receive ~p~n", [Res])
end.
main() ->
spawn(fun() -> parent() end).
first you need execute wy:main().
(1) if you execute myFunction ! msg22., you can find the process myFunction is still alive, and use this command erlang:process_info(whereis(myFunction), messages)., you can find the message msg22 is stored in the message queue.
(2) if you execute myFunction ! msg2., you can get this output
Message 2!
Receive {'DOWN',#Ref<0.0.0.108>,process,<0.48.0>,normal}
msg2
from this output you can know the process myFunction is died.
(3) if you execute myFunction ! msg1, the process will go into endless loop。
The other answers are almost correct. If you don't do a recursive call after receiving msg2 then the function will end and you will go back to the caller of myFunction/0. It will then depend on the caller what happens. If this is the last thing to do in the process then the process will terminate with reason normal, otherwise it will continue processing.
The thing to remember is that each call to receive will just process one message so if you want to keep processing messages then you must do repeated calls to receive. Hence the recursion in myFunction.
I have one process which sends a pause message to a gen_server like so:
Results = [gen_server:cast(Child, pause) ||
{Id, Child, _Type, _Modules} <- supervisor:which_children(?SERVER),
?IGNORE(Id) == false],
In my gen_server, I catch these messages in my handle_cast as follows:
handle_cast(pause, #state{task=#task{server=Serv,
service=Srv,
description=Desc}}=State) ->
lager:info("Suspending ~s, ~s, ~s.",[Serv, Srv, Desc]),
{noreply, State#state{suspended=true}};
handle_cast(Msg, State) ->
lager:error("Url Poller received unexpected cast message: ~p",[Msg]),
{noreply, State}.
What's really strange is that fairly frequently one of my gen_servers doesn't seem to receive the pause message -- I get no lager message and the process in question will not respond to subsequent attempts to pause (or resume).
Any ideas about what might be going on?
The gen_server is very simple, it uses erlang:send_after/3 to send itself a "poll" message. Upon receiving this poll message, if not paused, it hits a url and saves the response to an ETS and fires off another erlang:send_after/3 to poll again after an appropriate interval. If its paused, it simply fires off another erlang:send_after?3
All pause does is set the state to paused = true
Using observer, the stuck process shows that the current function is httpc:handle_answer and that the message queue is backing up
Sate Tab: Information "Timed out"
Tip "system messages are probably not treated by this process"
the top of the stack trace shows
httpc:handle_answer httpc.erl:636
I picked the code of httpc:handle_answer from github erlang otp inets http client:
(Note: it is not the same version as yours since the function goes from line 616 to 631)
handle_answer(RequestId, false, _) ->
{ok, RequestId};
handle_answer(RequestId, true, Options) ->
receive
{http, {RequestId, saved_to_file}} ->
?hcrt("received saved-to-file", [{request_id, RequestId}]),
{ok, saved_to_file};
{http, {RequestId, {_,_,_} = Result}} ->
?hcrt("received answer", [{request_id, RequestId},
{result, Result}]),
return_answer(Options, Result);
{http, {RequestId, {error, Reason}}} ->
?hcrt("received error", [{request_id, RequestId},
{reason, Reason}]),
{error, Reason}
end.
So the process is waiting for a message (coming after a call to httpc_manager:request(Request, profile_name(Profile) which has returned {ok, RequestId}), and this message does not come or it has a wrong format. Can you check the values of the parameters and the message queue?
headers which contained value other than string caused the httpc_handler exited. But after that, the caller hung at the 'receive' in httpc:handle_answer/3 forever since no message was sent to the caller.
you can test with this
Request1= {"http://www.google.com",[{"cookie",undefined}, {"test",123}],"application/x-www-form-urlencoded; charset=utf-8", <<"">>}.
httpc:request(post, Request1, [{timeout,1000}], []).
Good day,
I have a gen_server process which does some long-running state-updating tasks periodically in
handle_info:
handle_info(trigger, State) ->
NewState = some_long_running_task(),
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply, NewState}.
But when such task runs, then whole server gets unresponsive and any call to it leads to whole server crash:
my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
in function gen_server:call/2
How it is possible to avoid blocking of gen_server ?
And when one call my_gen_server:status() at any time, the result should be something like:
{ok, task_active}
execute the long running task in a separate process. Let this process inform the gen_server of its progress with the task (that is if the task's progress can be tracked) OR let the process complete the task or fail but at least inform the gen_server of the results of the task.
Let the gen_server be linked with the process doing this long running task, and let the gen_server know the PID or registered name so that in case of exit signals, it can isolate the death of that important process from the Rest.
handle_info(trigger, State) ->
Pid = spawn_link(?MODULE,some_long_running_task,[State]),
NewState = save_pid(Pid,State),
{noreply, NewState};
handle_info({'EXIT',SomePid,_},State)->
case lookup_pid(State) == SomePid of
false -> %% some other process
{noreply,State};
true ->
%% our process has died
%% what do we do now ?
%% spawn another one ?
%% thats your decision to take
....
....
{noreply,State}
end;
handle_info({finished,TaskResult},State)->
.....%% update state e.t.c.
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply,NewState}.
some_long_running_task(ServerState)->
....do work
....return results
This call does not lead to a crash, but simply to an exception which can be caught:
status() ->
try gen_server:call(my_gen_server, status)
catch
exit:{timeout,_} -> {ok, task_active}
end.
However, the call will remain in the server's queue, and after it finishes handling the current message, it will send a reply message: {ServerRef, Reply}, which should be discarded by the calling process.
The only way to avoid blocking of any process in Erlang (whether gen_server or not) is not to run blocking tasks on it. So another alternative could be to run your long tasks on a different process which only talks to your server, so nobody cares that it's blocked.
Normally if I'd like to have an Erlang process timeout I would use the following construct:
receive
Msg -> ok; %% handle message
after 60000 ->
%% Handle timeout and exit
end.
Is there a similar mechanism in the OTP servers such as gen_fsm? I will be spawning gen_fsm's for each active session with my application, and would like to have them exit if a timeout value for inactivity is exceeded after receiving a message.
I can write my own custom process if need be, but would prefer to use a gen_fsm if possible.
I dug some more and found the answer to my own question.
There is an optional fourth argument in message handler "Result"s that you can use which is a timeout.
so:
some_fsm_state({set, Val}, State) ->
NewState = do(Val, State),
{next_state, another_fsm_state, NewState, 5000};
another_fsm_state(timeout, State) ->
handle_timeout(State).
another_fsm_state({set, Val}, State) ->
%% more code that handles this state.
Once some_fsm_state is called, it transitions to the next state of "another_fsm_state" with a timeout of 5000ms. If not new message is received within 5000ms, then another_fsm_state(timeout, State) is called.
Clever OTP programmers. :)
It should be noted that this fourth element in the Results tuple can be hibernate. Please see Erlang documentation for more information.
Erlang - Hibernate
gen_fsm docs