It would because I would have a middleware that sets the current user in the process (and all middleware calls for one particular user/subscriber would be called within that same process).
We can just create a new process/task and await
until it’s done before continuing, so in terms of throughput you get the same result.
I had to find a solution in any case to move on. For every middleware that runs I create a new process if the user in the context is different than the user in the process (which happens when dealing with subscribers). So for one subscriber I’ll create multiple new processes, one process for one middleware call.
schema.ex
def middleware(middleware, _field, _object) do
middleware = [Middleware.Authenticate | [Middleware.Authorize | middleware]]
ensure_new_process(middleware)
end
defp ensure_new_process(middleware) do
Enum.map(middleware, &{Middleware.EnsureNewProcess, &1})
end
ensure_new_process.ex
defmodule MyAppWeb.Schema.Middleware.EnsureNewProcess do
@behaviour Absinthe.Middleware
alias MyApp.Authorizer
def call(resolution, middleware) do
case resolution.context[:current_user] do
nil ->
call_middleware(middleware, resolution)
current_user ->
if current_user.id == Authorizer.get_current_user().id do
call_middleware(middleware, resolution)
else
Task.await(Task.async(fn ->
Authorizer.put_current_user(current_user)
call_middleware(middleware, resolution)
end))
end
end
end
defp call_middleware({{mod, fun}, opts}, res) do
apply(mod, fun, [res, opts])
end
defp call_middleware({mod, opts}, res) do
apply(mod, :call, [res, opts])
end
defp call_middleware(mod, res) when is_atom(mod) do
apply(mod, :call, [res, []])
end
defp call_middleware(fun, res) when is_function(fun, 2) do
fun.(res, [])
end
end
I simplified the code above because it gets more ugly in reality, where I use the context as a cache for one variable that the authorizer module computes (with db calls) and would need to compute every time otherwise (as I keep creating new processes and so keep losing the state).