mszmurlo
mszmurlo

Reputation: 1339

How to catch GenServer.call(destination, ...) failure when destination process doesn't exist

In my application all logged users are represented by a GenServer, I call a UserAgent, which basically keeps in memory the state of each user. All those processes are registered across the cluster in a distributed registry based on Horde with a unique ID. Whenever the user does some action, the client application sends the action to be performed along with the user_id. On server side, the controller checks the parameters (mandatory, optional, syntax, etc) and eventually calls UserAgent.the_action(user_id, other_params). The the_action(...) function simply sends a message to the server with the action to be performed: GenServer.call(via_tuple(id), {:the_action, params}).

In some cases, the UserAgent referenced by user_id doesn't exist any more, for example because the user had been inactive for some time and the process had been cleaned out (the sesion had expired) or because parts of the cluster are not reachable at that moment (imaginary use case for the moment). In those situations the call GenServer.call(via_tuple(id), {:the_action, params}) results in the error below which also crashes the HTTP endpoint process (#PID<0.1359.0> below) which in turn results in a 500 HTTP error (the dump api call, well, dumps the state of a process for debug purposes):

[error] #PID<0.1359.0> running AppWeb.Endpoint (connection #PID<0.1358.0>, stream id 1) terminated
Server: localhost:4001 (http)
Request: GET /api/v1/dump/5f534b99d6ca3fe1ff6d2f78
** (exit) exited in: GenServer.call({:via, Horde.Registry, {App.DReg, "5f534b99d6ca3fe1ff6d2f78"}}, :dump, 5000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated \
        with the given name, possibly because its application isn't started

I just can't figure out how to intercept this error. I actually ended up with calling Horde.Registry.lookup(App.UserAgent.via_tuple(user_id)) in the client part of the UserAgent and then calling GenServer.call() with the returned pid` or returning an error to the controller if the process was not found.

I was wondering if there was a better way.

Upvotes: 1

Views: 670

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

This is how GenServer.call/3 handles errors in . It nevertheless calls whereis/1, so you might either call whereis/1 yourself or replicate logic from the code I linked or use Kernel.SpecialForms.try/1 to catch the exception.

Upvotes: 3

Related Questions