2Sjch8AT

2Sjch8AT

I have a GenServer which is using an array of other GenServer to fullfil its jobs, each of them being connected to different channels for sending data. The first GenServer is acting like a dispatcher. When a call on the first GenServer is invoked, one of the GenServer in the array is chosen and a call is performed on it.

I would like the GenServer (dispatcher) to be asynchronous: I don’t want to block while the data is sent to one of the GenServer contained in the array. But I would also ilke to check for errors, so using GenServer.cast is not an option.

What options do I have? Does it make sense to use spawn and GenServer.call for each incoming request coming to the dispatcher?

Showing Posts 1 to 4

amikhailov

amikhailov

Does it make sense to use spawn and GenServer.call for each incoming request coming to the dispatcher?

Erlang rpc implements asynchronous calls exactly like this: otp/lib/kernel/src/rpc.erl at 613b5f890a5bc13aaf64cc31b535262a40eba721 · erlang/otp · GitHub

So yes, I think it’s the way to go.

peerreynders

peerreynders

That doesn’t make the dispatcher asynchronous - and this tactic is largely a defensive move by a client process forced to make a synchronous call when it doesn’t want to be blocked.

But not wanting to be blocked the client process still has to:

  • eventually process the result from the terminating proxy-process
  • monitor that proxy-process in case it terminates before sending the result

Given that you are implementing the dispatcher you might as well cut out the “proxy-process”:

  • Have the client process cast/2 the request to the dispatcher (include a reference from Kernel.make_ref/0 in the request that the dispatcher can return in the response information to make it easier to correlate a response to a request).
  • Have the client process Process.monitor/2 the dispatcher.
  • If the client receives a :DOWN (via handle_info/2) message with the dispatcher-monitor-reference and dispatcher PID, inspect the reason
  • :noproc - there is no process with pid
  • :noconnection - can’t reach specified node
  • :normal - no worries, sent result is still in the queue (provided a result was sent)
  • other non-normal exit reasons
  • When the dispatcher has completed the request have it cast/2 the response information (including the correlation reference) back to the client process.
  • When the client receives the response information from the dispatcher, immediately Process.demonitor/2 the dispatch-monitor-reference (specifying [:flush] for opts to purge the :DOWN message that could still be in the message queue) and then process the response information.

See also: The need for monitoring

2Sjch8AT

2Sjch8AT OP

That doesn’t make the dispatcher asynchronous

Could you explain to me why?

Thanks for your detailed answer.

peerreynders

peerreynders

The intent of call/3 is to implement a synchronous protocol on top of a native asynchronous one.

call boils down to the client sending a request and then blocking until a reply for that exact request arrives - ignoring any other messages that may arrive in the meantime.

Now typically the server processes the request immediately in handle_call/3 and responds with a :reply.

So the intent behind call/handle_call is to implement a synchronous protocol.

Now the server has some wiggleroom as it can finish handle_call with :noreply to delay emitting a reply until later with reply/2 but that doesn’t change the synchronous client experience (the server might do this to stop the same client from sending additional requests).

The “spawn to call” tactic is used as a countermeasure by a client process which doesn’t wish to have the “synchronous experience” that call enforces.

Using “spawn to call” within the “service” (rather than on the client-side) may give the client process the illusion of asynchronous communication (while still leaving the burden of setting up the monitor and dealing with monitor messages) - but call still implements a synchronous protocol; for what purpose?

(My guess is that the dispatcher core has been designed/written in terms of handle_call which means that the dispatcher implementation is in fact synchronous. Such an implementation could be, not necessarily, but quite possibly blocked while processing a single request rather than servicing other requests asynchronously.)

If the dispatcher is asynchronous and the clients need to deal with it asynchronously then simply have the dispatcher serve/implement cast/handle_cast instead (while the dispatcher casts its reply back to the client) to fully reveal the dispatcher’s asynchronous nature.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
thiagogsr
** (ArgumentError) expected :max_attempts to be a positive integer, got: {:@, [line: 10, column: 19], [{:max_attempts, [line: 10, column:...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews