How to handle this Genserver call try catch message queue - wrong message received to handle_info

I found this from erlang genserver documentation, but couldn’t firgure out how to handle this.

Before OTP 24, if the caller uses (try…)catch to avoid process exit, and the server happens to just be late with the reply, it may arrive to the process message queue any time later. The calling process must therefore after catching a time-out exit be prepared to receive garbage message(s) on the form {reference(), _} and deal with them appropriately (discard them) so they do not clog the process message queue or gets mistaken for other messages.

did you forget to include some code here?

I have a sensor application umbrella project which using Circuits_uart to read sensors data and send those data to another elixir project via websocket and save it to postgresql database.

I make a minimum psudo code of my project to see my current issue.

defmodule Sensor.Application do
  use Application

  @impl true
  def start(_type, _args) do
    children = [
      Sensor.Config,
      Sensor.Threshold,
      %{
        id: Sensor.SocketClient,
        start: {Sensor.SocketClient, :start_link, []}
      },
      Sensor.Command,
      Sensor.Dummy,
      Sensor.Counter,
      Sensor.Detector,
      Sensor.Senosr1Fault,
      Sensor.Senosr2Fault,
      Sensor.Senosr3Fault,
      Sensor.Senosr4Fault,
      Sensor.Senosr5Fault,
      Sensor.Senosr6Fault,
      Sensor.Senosr7Fault,
      Sensor.Heartbeat,
      Sensor.SendDataToBackend
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_all, name: Sensor.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
defmodule Sensor.Command do
    def get_data(sensor) do
        GenServer.call(__MODULE__, {:get_data, sensor})        
    catch
        :exit, _ ->
            {:ok, ""}
    end

    def handle_call({:get_data, sensor}, _from, state) do
        Logger.info("GetCurrentStatus: #{inspect(sensor)}")
        status = get_status(sensor, state)
        {:reply, status, state}
    end
end

I did call the genserver call and wait for default timeout about 5000ms to get result otherwise.

I don’t want to throw or get exception that’s why I use catch and return {:ok, “”}. But I can safe exception and starting getting unexpected callback to handle_info with unexpected parameters is sending to any random genserver handle_info callback. This issue is already mention is the Erlang documents but I didn’t found any solution for this.

Erlang document

Note

Before OTP 24, if the caller uses (try…)catch to avoid process exit, and the server happens to just be late with the reply, it may arrive to the process message queue any time later. The calling process must therefore after catching a time-out exit be prepared to receive garbage message(s) on the form {reference(), _} and deal with them appropriately (discard them) so they do not clog the process message queue or gets mistaken for other messages.

Starting with OTP 24, gen_server:call uses process aliases, so late replies will not be received.

I saw this solution from Erlang DOCs.

Note

This callback is optional, so callback modules need not export it. The gen_server module provides a default implementation of this function that logs about the unexpected Info message, drops it and returns {noreply, State}.

This function is called by a gen_server process when a time-out occurs or when it receives any other message than a synchronous or asynchronous request (or a system message).

Info is either the atom timeout, if a time-out has occurred, or the received message.

For a description of the other arguments and possible return values, see Module:handle_call/3.

Types
Request = term()
From = from()
State = term()
Result = {reply,Reply,NewState}
  | {reply,Reply,NewState,Timeout}
  | {reply,Reply,NewState,hibernate}
  | {reply,Reply,NewState,{continue,Continue}}
  | {noreply,NewState}
  | {noreply,NewState,Timeout}
  | {noreply,NewState,hibernate}
  | {noreply,NewState,{continue,Continue}}
  | {stop,Reason,Reply,NewState}
  | {stop,Reason,NewState}
 Reply = term()
 NewState = term()
 Timeout = timeout()
 Continue = term()
 Reason = term()

https://www.erlang.org/doc/man/gen_server#Module:handle_call-3

1 Like