"Forwarding" of gen_server calls?

Ulf Wiger etxuwig@REDACTED
Fri Sep 17 10:40:17 CEST 1999


On Thu, 16 Sep 1999, Jim Larson wrote:

jim>What would be nice is a "tail call" function in the gen_server
jim>package that would allows you to forward the From "continuation"
jim>to another gen_server module, but returns to its caller immediately.
jim>
jim>    handle_call(Request, From, State) ->
jim>        %% ... the same pre-processing goes on ...
jim>        %% This call won't block
jim>        case gen_server:tail_call(S2, Arg, From) of
jim>            ok ->
jim>                {noreply, NewState}; % S2 will reply to our caller
jim>            {error, Reason} ->
jim>                %% error looking up S2
jim>                {reply, {error, SomeReason}, SomeState}
jim>        end.
jim>
jim>The implementation for the simplest clause of tail_call would be
jim>something like:
jim>
jim>    tail_call(Pid, Request, From) when pid(Pid), node(Pid) == node() ->
jim>        Pid ! {'$gen_call', From, Request},
jim>        ok.

What you're proposing is quite doable in erlang 47.4.0, but becomes tricky
in the commercially available R5B release. I'll explain why below.

Another way to do it (as you also point out) would be to let the client
function handle the redirect:

req(To, Request) ->
   case gen_server:call(To, Request) of
      {redirect, NewTo, NewRequest} -> 
         req(NewTo, NewRequest);
      Reply ->
         Reply
   end.

It leads to a few more messages, but unless you have extreme response-time
requirements, or the calls are passed across node limits, it probably
doesn't matter much. Since it's common practice to implement API functions
as "wrappers" around the gen_server:call() (don't let the implementation
show in the interface), the client won't know that it's been redirected.


jim>[Okay, so it doesn't quite handle all error cases, since the original
jim>gen_server caller will only catch 'EXIT' messages from the Pid of
jim>the server it originally called.  I'm sure there are ways to deal
jim>with this.]

Actually, in erlang-47.4.0, the client only detects timeouts and nodedowns
in gen:call(). However, in R5B (the latest commercial release), and in the
upcoming Open Source release, gen:call() uses a new one-way monitor
function to make sure the server doesn't crash while processing the call.
This is needed in order to avoid unnecessary latency.

The problem, from your perspective, becomes that the client will continue
to monitor the first server, even though the call is passed to another
server. A function like gen_server:tail_call() wouldn't be able to do much
about that, since it would execute in a process other than the one doing
the monitoring.

jim>However, these workarounds are unappealing.  Am I overlooking any
jim>other options or existing infrastructure for accomplishing what I
jim>want?

It's difficult to tell without knowing the specifics of the problem, but
sometimes it's possible to let the client to part of the pre-processing.
You can also let the client read an ETS table (e.g. maintained by S1) in
order to determine which server to address.

/Uffe

Ulf Wiger, Chief Designer AXD 301      <ulf.wiger@REDACTED>
Ericsson Telecom AB                          tfn: +46  8 719 81 95
Varuvägen 9, Älvsjö                          mob: +46 70 519 81 95
S-126 25 Stockholm, Sweden                   fax: +46  8 719 43 44




More information about the erlang-questions mailing list