How to structure MQ consumer loop in Erlang/OTP? - erlang

I need to create simple application that consumes message queue and asynchronously handles messages using Erlang/OTP. Consider this pseudo-example in Golang:
var queue chan
func main() {
for req := range queue {
go handleRequest(req) //handle asynchronously
}
}
How to correclty structure this following OTP principles?
I've been looking for gen_server, but in this case where do I define my loop resursive?
Also, how can I start asynchronous handle? Should I create another supervisor and use supervisor:start_child on every new message?

The gen_server module in the standard library defines the recursive loop for you. The only thing you need to do is to implement callback functions to handle messages. If the message queue is sending Erlang messages to your gen_server process, you'd do something like:
handle_info({incoming_request, Request}, _From, State) ->
async_handle_request(Request),
{noreply, State}.
To handle the requests asynchronously, async_handle_request would start a process for each incoming request. There are two ways of doing that: either just spawn a process, or start each process under a simple_one_for_one supervisor. The differences come down to error handling and shutdown behaviour. What do you do if handling a request fails? Do you ignore the error, or let it propagate to the gen_server process, or do you have the supervisor restart the process and retry the request?
This question explains when you might use a simple_one_for_one supervisor. If you just want to spawn a process, that would look like this:
async_handle_request(Request) ->
spawn_link(fun() -> handle_request(Request) end).
And then the actual request handling logic is implemented in handle_request.

Related

How do I use handle_cast and handle_call in Erlang gen_server?

As far as I understand, I should use handle_cast for asynchronous requests, by sending messages, then react to response via receive..end on client side.
Here is an example:
Pid ! {auth_user, {User, Password}}.
For synchronous requests, when I want to wait when gen_server sends a response, I must explicitly call the respective function from the module like this:
{ok, Result} = auth_server:auth_user({User, Password}).
Are the statements from above are correct?
If the latter is correct, why then I need a construction like this if I already have an exported function auth_user?
handle_call({authenticate_user, {Login, Password}}, _From, _) ->
{reply, {}, {}}.
You don't interact with a gen_server by sending messages directly like that. You should use gen_server:call or gen_server:cast like this:
Response = gen_server:call(Pid, {auth_user, {User, Password}})
call / cast will handle sending the message to the Pid (and receiving a response for call) itself, along with many edge cases like the process crashing before responding to your request, etc.
If the latter is correct, why then I need a construction like this if I already have an exported function auth_user?
gen_server is useful when you want to keep some state stored between multiple calls. For example, if you want to keep a global counter of the number of pageviews in your app in memory, you should use gen_server, store the counter as the state of the and make a cast or call to it on each request. If you don't have a state to keep, you should use normal functions.

Erlang: observe gen_server state without disturbing timeout

I have a gen_server that mimics a gen_fsm, (don't ask me why...), a gen_server:call will cause this gen_server from the current state to transfer to the next state, if no gen_server:call invoked in a certain time, the gen_server terminates.
To be more specific, the life of the gen_server looks like this:
state_1 -> state_2 -> ... -> state_N -> terminated
when the server is at state_i, if there is no gen_server call invoked on this server, after t_i seconds, the server will go to state terminated, of cause, this is achieved by using {reply, Reply, NewState, t_i} as the return of the handle_call/3.
The problem of the method is that, I cannot retrieve some information from this gen_server, for to do that, I need to invoke gen_server:call on it, which would mess the timeout up.
One possible workaround is to put the last state transfer time stamp into the state, after each retrieval call, reset the new timeout to a appropriate value, a prototype looks like this:
handle_call(get_a, _From, #state{a = 1,
state = state_2,
%% this is the timestamp when the server transfered to state_2
unixtime = T1
}=S) ->
Reply = S#state.a,
NewTimeout = t_2 - (current_unixtime() - T1),
{reply, Reply, S, NewTimeout};
In this way, I can get the effect I want, but it's ugly, is there any better way to to this?
If you want to set timeouts independently of other events like calls it's probably best to use a timed message.
When you set the timeout use erlang:send_after/3:
TimerRef = erlang:send_after(10000, self(), my_timeout).
You can cancel your timeout any time with erlang:cancel_timer/1.
erlang:cancel_timer(TimerRef).
Receive it with handle_info:
handle_info(my_timeout, State) ->
If you need multiple such timeouts, use a different message, or if you have the possibility of some sort of race condition and need further control, you can create unique references with erlang:make_ref/0 and send a message like {Ref, my_timeout}.
Watch out for the edge cases - remember you could cancel a timer and then still receive it unexpectedly (because it's in your message queue when you cancel), and you don't make them unique (as suggested above using a reference) you could be expecting a timeout, and get it early, because it's the previous one that entered your message queue, etc (where as with a reference you can check it is the latest set). These things are easy to deal with, but watch out for them.

Is there a way to make an asynchronous call to a gen_server in Erlang?

E.g. suppose I have a module that implements gen_server behavior, and it has
handle_call({foo, Foo}, _From, State) ->
{reply, result(Foo), State}
;
I can reach this handler by doing gen_server:call(Server, {foo, Foo}) from some other process (I guess if a gen_server tries to gen_server:call itself, it will deadlock). But gen_server:call blocks on response (or timeout). What if I don't want to block on the response?
Imaginary use-case: Suppose I have 5 of these gen_servers, and a response from any 2 of them is enough for me. What I want to do is something like this:
OnResponse -> fun(Response) ->
% blah
end,
lists:foreach(
fun(S) ->
gen_server:async_call(S, {foo, Foo}, OnResponse)
end,
Servers),
Result = wait_for_two_responses(Timeout),
lol_i_dunno()
I know that gen_server has cast, but cast has no way to provide any response, so I don't think that that's what I want in this case. Also, seems like it should not be the gen_server's concern whether caller wants to handle response synchronously (using gen_server:call) or async (does not seem to exist?).
Also, the server is allowed to provide response asynchronously by having handle_call return no_reply and later calling gen_server:reply. So why not also support handling response asynchronously on the other side? Or does that exist, but I'm just failing to find it??
gen_server:call is basically a sequence of
send a message to the server (with identifier)
wait for the response of that particular message
wrapped in a single function.
for your example you can decompose the behavior in 2 steps: a loop that uses gen_server:cast(Server,{Message,UniqueID,self()} with all servers, and then a receive loop that wait for a minimum of 2 answers of the form {UniqueID,Answer}. But you must take care to empty your mail box at some point in time. A better solution should be to delegate this to a separate process which will simply die when it has received the required number of answers:
[edit] make some correction in the code now it should work :o)
get_n_answers(Msg,ServerList,N) when N =< length(ServerList) ->
spawn(?MODULE,get_n_answers,[Msg,ServerList,N,[],self()]).
get_n_answers(_Msg,[],0,Rep,Pid) ->
Pid ! {Pid,Rep};
get_n_answers(_Msg,[],N,Rep,Pid) ->
NewRep = receive
Answ -> [Answ|Rep]
end,
get_n_answers(_Msg,[],N-1,NewRep,Pid);
get_n_answers(Msg,[H|T],N,Rep,Pid) ->
%gen_server:cast(H,{Msg,Pid}),
H ! {Msg,self()},
get_n_answers(Msg,T,N,Rep,Pid).
and you cane use it like this:
ID = get_n_answers(Msg,ServerList,2),
% insert some code here
Answer = receive
{ID,A} -> A % tagged with ID to do not catch another message in the mailbox
end
You can easily implement that by sending each call in a separate process and waiting for responses from as many as required (in essence this is what async is about, isn't? :-)
Have a look at this simple implementation of parallel call which is based on the async_call from rpc library in OTP.
This is how it works in plain English.
You need to make 5 calls so (in the parent process) you spawn 5 child Erlang processes.
Each process sends back to the parent process a tuple containing its PID and the result of the call.
The tuple can be only constructed and send back only when the desired call has been completed.
In the parent process you loop through responses in the receive loop.
You can wait for all responses or just 2 or 3 out of the started 5.
The parent process (which spawns the worker processes) will eventually receive all responses (I mean those you want to ignore). You need a way to discard them if you don't want the message queue to grow infinitely. There are two options:
The parent process itself can be a transient process, created only for the call to spawn the other 5 child processes. Once the desired amount of responses is collected it can send the response back to a caller and die. Messages send to the died process will be discarded.
The parent process can continue receiving messages after it has received the desired amount of responses and simply discard them.
gen_server do not have a concept of async calls on client side. It is not trivial how to implement in consistently because gen_server:call is a combination of monitor for server process, send request message and wait for either answer or monitor down or timeout. If you do something like what you mentioned you will need to deal with DOWN messages from server somehow ... so hypothetical async_call should return some key for yeld and also an internal monitor reference for a case you are processing DONW messages from other processes... and do not want to mix it with yeld errors.
Not that good but possible alternative is to use rpc:async_call(gen_server, call, [....])
But this approach have a limitation in calling process will be a short lived rex child, so if your gen server use caller pid somehow other than send it a reply logic will be broken.
gen_sever:call to the process itself would surely block until timeout. To understand the reason, one should be aware of the fact that gen_server framework actually combine your specific code together into one single module, and gen_server:call would be "translated" as "pid ! Msg" form.
So imagine how this block of code takes effect, the process actually stay in a loop keeping receiving messages, and when the control flow run into a handling function, the receiving process is temporarily interrupted, so if you call gen_server:call to the process itself, since it is a synchronous function, it waits for response, which however would never come in until the handing function returns so that the process can continue to receive messages, so the code is in a deadlock.

Erlang: why is my process still running after unregistering process ID?

Even though I unregister sts, my spawned process is not stopped. How can I stop it not using gen_server?
start() ->
case whereis(sts) of
undefined ->
PidA = spawn(dist_erlang, init,[]),
register(sts, PidA),
{ok,PidA};
_ ->
{ok,whereis(sts)}
end.
stop() ->
case whereis(sts) of
undefined ->
already_stopped;
_ ->
unregister(sts),
stopped,
end.
Using unregister does not stop the process. Stopping the process does, however, unregister it. So instead of using unregister here, use erlang:exit/2
stop() ->
case whereis(sts) of
undefined ->
already_stopped;
Pid ->
exit(Pid, normal), % Use whatever exit reason you want
stopped
end.
All that being said, you should really be using the OTP process behaviours (like gen_server), as they make process management much easier. With an OTP process, you can instead call the process and tell it to stop, so that when you get your reply it has already stopped. Otherwise your exit message may take some time to get through.
unregister does not stop the process. It just removes binding between process id and given atom.
You need to remember that stop/0 function is run in context of process that called this function, and not the gen_server itself. Actually (almost) only way to interact with some process is to send it a message. So you could implement your stop/0 function like this:
%% send stop message to `sts` server
stop() ->
gen_server:cast(sts, stop).
%% [...]
handle_cast( OtherCastMessages, State) ->
%% handel other cast messages ;
%% [...] ;
%% handle stop message
handle_cast( _Message = stop, State) ->
{stop,
_Reason = normal,
State}. % return result that stops server
%% [...]
terminate(_Reason = normal, State) ->
%% could do some cleanup in this callback
ok.
So to stop server you have to return special tuple from one of the behaviour functions. You can read more about this here. And of course trigger one of behaviour functions, you have to send message to your server with gen_server:cast or gen_server:call (or just send a message and handle it with handle_info). What to use is your decision. Finally terminate2 is called (no matter which callback returned tuple with stop atom), where you could do some cleanup with your state.
Of course you could unregister your process in terminate callback, but when process dies the unregistration is handled automatically.

Erlang Concurrency Model

This could be a very basic question but is Erlang capable of calling a method on another prcoess and wait for it to repond back with some object type without sleeping threads?
Well, if you're waiting for an answer, the calling process will have to sleep eventually… But that's no big deal.
While processes are stuck on the receive loop, other processes can work. In fact, it's not uncommon to have thousands of processes just waiting for messages. And since Erlang processes are not true OS threads, they're very lightweight so the performance loss is minimal.
In fact, the way sleep is implemented looks like:
sleep(Milliseconds) ->
receive
% Intentionally left empty
after Milliseconds -> ok
end.
Yes, it is possible to peek into the mailbox if that is what you mean. Say we have sent a message to another process and now we want to see if the other process has sent something back to us. But we don't want to block on the receive:
receive
Pattern -> Body;
Pattern2 -> Body2
after 0 ->
AfterBody
end
will try to match against the Pattern and Pattern2 in the mailbox. If none matches, it will immediately time out and go to AfterBody. This allows you to implement a non-blocking peek into the mailbox.
If the process is a gen_server the same thing can be had by playing with the internal state and the Timeout setting when a callback returns to the gen_server's control. You can set a Timeout of 0 to achieve this.
What am getting from the question is that we are talking of Synchronous Message Passing. YES ! Erlang can do this perfectly well, its the most basic way of handling concurrency in Erlang. Consider this below:
rpc(Request, To)->
MySelf = self(),
To ! {MySelf,Request},
receive
{To,Reply} -> Reply
after timer:seconds(5) -> erlang:exit({error,timedout})
end.
The code above shows that a processes sends a message to another and immediately goes into waiting (for a reply) without having to sleep. If it does not get a reply within 5 seconds, it will exit.

Resources