How do you do selective tricks in gen_servers?

I ported most of my application to OTP behavior, but I'm stuck. I cannot figure out how to do selective transfers using gen_server. If none of the callback function suggestions matches the message, but puts the message in the mailbox, it does not work!

Now, wherever I go, people welcome a selective welcome. Everywhere I go, people welcome OTP. Is it really true that you cannot have both at once? Doesn't that sound like a serious, fixable flaw?

How do erlang programmers handle this?

EDIT (response to zed comment):

Here is an example where I would like to see a list of integers printed in sorted order:

-module(sel_recv). -behaviour(gen_server). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([test/0]). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). test() -> gen_server:cast(?MODULE, test). init([]) -> {ok, 0}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(test, _State) -> lists:map(fun(N) -> gen_server:cast(?MODULE, {result, N}) end, [9,8,7,6,5,4,3,2,1]), {noreply, [1,2,4,5,6,7,8,9]}; handle_cast({result, N}, [N|R]) -> io:format("result: " ++ integer_to_list(N) ++ "~n"), {noreply, R}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. 

Of course, in my real application, there are timer delays, and messages that need to be processed in order alternate with other messages. In particular, I send HTTP requests, sometimes a lot at once, sometimes one at a time with an interval between them. In any case, I need to collect them in order.

+7
erlang
source share
5 answers

Gen_server is probably not the best choice for this. One thing you can do is get all the messages in the buffer list and implement a selective trick:

 handle_cast(test, _State) -> ... {noreply, {[1,2,4,5,6,7,8,9], []}}; handle_cast({result, N}, {Wait, Buff}) -> {noreply, handle_results(Wait, [N|Buff])}. handle_results([], Buff) -> {[], Buff}; handle_results([W|WTail] = Wait, Buff) -> case lists:member(W, Buff) of true -> io:format("result: " ++ integer_to_list(W) ++ "~n"), handle_results(WTail, Buff -- [W]); false -> {Wait, Buff} end. 
+4
source share

"plain_fsm" will allow you to perform selective reception while maintaining OTP compatibility.

http://github.com/esl/plain_fsm

+7
source share

No, gen_server is not designed to handle selective requests, each request is processed as it arrives. This is actually a complex problem, since Erlang requires that all templates be known at compile time, there is no "template object".

I agree that gen_fsm is probably not for you, as it does not accept various messages arriving in any order before you receive an explosion in a number of states. This was one of the reasons we added a selective trick that allows you to safely ignore uninteresting messages, leaving them for later.

Which OTP are you most interested in?

+4
source share

Perhaps you really want to use gen_fsm. This behavior is usually chosen for protocols where the protocol has certain states and requires processing requests differently depending on what state it is in.

But back to gen_server, we use gen_server for its functions. It subscribes to system events to download code and gives you a code_change callback. This causes sasl crash reports. You get a well-known structure that helps maintain code. Most importantly, it implements a well-designed protocol for synchronous calls.

It's hard to say if OTP behavior is right for you in this case.


Given the comment, this sounds like gen_server is wrong for you. When I use the gen_server behavior, I think of it as a boss in a company. Everyone wants to talk with the boss, so it’s in the interests of the company to make the boss able to quickly and efficiently delegate jobs so that he doesn’t allow people to sit and wait rather than work.

Gen_server can delegate using the From parameter. To do this, return {no_reply, State} and pass the From parameter to the delegate. The delegate uses gen_server:reply/2 to answer the original call.

This method of using gen_server may be for you. Begin the “simple” process with which you use the get-end for selective reception. If this is truly independent work, gen_server can ignore it (fire and forget). If he wants to know if it is complete, you can return the gen_server:cast/2 message to it from the started "simple" process.

If you want to block gen_server while doing selective tricks, then you might get the idea to start the process and wait for it to die before returning. Selective reception is a linear search for O (n) in messages in the order in which they were received. This way, each selective trick will scan all messages in the queue, which may be high for the popular gen_server.

No company should work only where there are bosses. There is no erlang application that should only have gen_servers.

+3
source share

Just because you cannot use gen_server for one of your modules does not mean that you are not using OTP. All callback modules implement a receive block for you, which prevents the use of selective queries. There is no reason why you cannot implement your own service that processes selective receipts. And this does not mean that you did not do this using the OTP method.

You can still manage your service as a supervisor with all the benefits that are provided.

+2
source share

All Articles