Receive Message in GenServer.handle_call

Hi there,

hope somebody could help with this problem:

We have a process that is interacting with a system of one of our partner companies. They use Stomp/Websocket for business transactions.

The Interaction should not overlap with other transactions, that’s why I decided to put it into a GenServer and use GenServer.call.

The transaction consists of sending a request to a stomp-topic and then waiting for a response another topic. We use Websockex to send and receive. My idea was that my GenServer.handle_call calls Websockex instance to send and Websockex instance will send a message to the Genserver when it receives a response on the other topic.

defmodule MyGenserver do
  ...

  def handle_call(...) do
    WebsockexInst.send_message(xxx)
    receive do
     {:response, ....} ->
       react_on_response      
    end
  end
end

But seems like Genserver does not like to receive arbitrary messages: XXX received unexpected message in handle_info/2: {:some_message}

The whole transaction is synchronous: HTTP-Request → RestApi → GenServer.call > RestApi → HTTP-Response
I think it would be easy if it would run asynchronous, but I don’t see a possibility for that.

So the question is: Is it possible to configure GenServer to be able to receive a message inside handle_call. Is there something I could change to sync my GenServer with the Websockex Process?

Cheers
Marcus

It depends WHERE the message is received. If you do it as you showed in you example then it should work where you send a request and expect and handle ONE reply. If however you receive multiple replies or are sent messages when not in a handle_call or handle_cast, or for that matter handle_info, then the message is received in the GenServer top receive loop which you do not control. In the GenServer top loop some specific messages are recognised:

  • OTP system messages which are handled internally
  • the specially formatted messages coming GenServer.call and GenServer.cast which result in calls to handle_call and handle_cast
  • everything else which results in calls to handle_info

This is baked into the GenServer internals and is how the GenServer is defined to work. Note that if you so a synchronous request to another process the GenServer will block in that receive and cannot handle any other requests in the meantime as Erlang processes have only a single thread of execution.

In some cases it might be better to communicate asynchronously and handle the replies with handle_info and so not block the server. However that case you are wandering towards state machines which are another matter.

4 Likes