Search code examples
elixirgen-server

Processing data under load with GenServer (gen_server)


I am testing the following pattern in a GenServer:

  def handle_info({:tcp, _, data}, s) do
    # IO.puts "\nrx: \n#{Base.encode16(data)}\n"

    extra = _proc_data(<<s.extra::binary, data::binary>>)

    :inet.setopts(s.socket, active: :once)

    {:noreply, %{s | extra: extra}}
  end

There is a problem when data comes in fast, and i'm unable to update state before :inet.setopts(s.socket, active: :once) releases new data

Must {:noreply, %{s | extra: extra}} be the last line for handle_info, or can I perform the :inet.setopts(s.socket, active: :once) last?

Is there a better way to do this?


Solution

  • One technique I've used in the past is to send the data to another GenServer for processing. This would allow the current process to call :inet.setopts(s.socket, active: :once) faster.

    def handle_info({:tcp, _, data}, s) do
        # IO.puts "\nrx: \n#{Base.encode16(data)}\n"
    
        GenServer.cast(s.processor, {:data, data})
    
        :inet.setopts(s.socket, active: :once)
    
        {:noreply, s}
    end
    

    Processor:

    defmodule DataProcessor do
      use GenServer
    
      def start_link(opts), do: GenServer.start_link(__MODULE__, [], opts)
    
      def init(_), do: {:ok, %{}}
    
      def handle_cast({:data, data}, state) do
        extra = _proc_data(s.extra <> data)    
        {:noreply, %{state| extra: extra}}
      end
    
      defp _proc_data(data) do
        ...
      end
    end