I have GenServer setup in a module. One of the handle_call/3
returns {:no_reply, state}
. Every time after executing the call a timeout error raises. Can someone tell me what I am doing wrong here? This is the first time I am trying GenServer.
My code looks like this:
defmodule TestApp.Message do
alias TestApp.Message
use GenServer
import Logger
def init(args) do
{:ok, args}
end
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def handle_call({%{"message" => message}, state}, _sender, _current_state) do
info("message")
{:noreply, state}
end
end
This is the error message:
{:timeout, {GenServer, :call, [MyApp.Message, {%{"message" => message}, %{id: 1, sender: "user1}}, 5000]}}
The {:noreply, state}
return value tells the behaviour not to send a reply to the GenServer.call
. As it has built-in timeout of 5 seconds (which can be changed) then it will timeout when a reply does not arrive within that time. You need to explicitly send back a reply by returning {:reply, reply, state}
.
1 Like
I am planning to set up a connection with an iOT device, which does not expect a reply there. In such cases what can I do?
You need to use handle_cast in this case…
2 Likes
Yes, one way is to use GenServer.cast/handle_cast. One problem with that is that is gives no guarantees of the destination being there or the message arriving as it literally uses :erlang.send internally. For more safety you could use GenServer.call/handle_call and return the reply :ok. NB that this is doing a synchronous call with all that it costs but you will at least be certain the message has arrived and has been processed.
It’s all a matter of deciding what you need/want and are willing to pay for it. TANSTAAFL
2 Likes
In other words, when you use handle_call
, your caller (the process that calls GenServer.call
) expects a reply. One can return {:noreply, state}
from handle_call
, but only if the reply is computed asynchronously and then sent later with GenServer.reply
.
As @rvirding and @kokolegorille wrote, you have two options:
-
Return a simple reply like {:reply, :ok, state}
. This ensures that GenServer.call
will return :ok
only after the operation that you perform in the GenServer
completes. This is, generally, the best way, as it ensures that whatever you wanted to execute actually does execute.
-
Alternatively, if you really don’t care about the answer, and also you don’t need to guarantee that the GenServer
actually performed the operation, you can use GenServer.cast
and handle_cast
.