I have made an experimental system (Frankenstein?) where a GenServer initialises and provides the necessary state for handle_call’s, but the handle_call’s itself doesn’t return the value of the computations, but instead returns a closure that is then executed/waited for by the caller.
defmodule Test do
use GenServer
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, options)
end
def get_foo(foo_id) do
with {:ok, task} <- GenServer.call(__MODULE__, {:get_foo, foo_id}) do
task
|> Task.async()
|> Task.await()
end
end
def init(options) do
send(self(), {:load, options})
{:ok, nil}
end
def handle_info({:load, _options}, _state) do
state = SomeModule.expensive_state_load()
{:noreply, state}
end
def handle_call({:get_foo, foo_id}, _from, state) do
task = fn -> FooModule.get_foo(foo_id, state) end
{:reply, {:ok, task}, state}
end
end
question: is there an equal easy method to do what I want: to avoid blocking a genserver queue containing necessary “global state”?
also worth mentioning that this is not for use in production code. It’s used in exunit tests, which have a configureable amount of concurrent tests.