Query System

View Source

< Repo | Index | Hexagonal Architecture >

Automatic N+1 query batching via dependency analysis. The query system analyzes effect dependencies in a query do block and batches independent data fetches together — using FiberPool fibers to execute them concurrently.

Defining fetch operations

defmodule MyApp.Users do
  use Skuld.Query

  deffetch get_user(id :: String.t()) :: {:ok, User.t()} | {:error, term()}
  deffetch get_subscription(user_id :: String.t()) :: {:ok, Subscription.t()} | {:error, term()}
end

Wiring executors

An executor handles a batch of operations:

defmodule MyApp.UserExecutor do
  @behaviour Skuld.Query.Executor

  def execute(tagged_ops) do
    # tagged_ops = [{ref, %GetUser{id: id}}, ...]
    # Return results keyed by ref
    results = for {ref, op} <- tagged_ops, into: %{} do
      {ref, Repo.get!(User, op.id)}
    end
    {:ok, results}
  end
end

Skuld.Query.with_executor(MyApp.Users, MyApp.UserExecutor)

Query blocks

A query do block expresses data dependencies naturally. The system analyzes them and batches independent fetches:

comp do
  query do
    user <- MyApp.Users.get_user(user_id)
    sub <- MyApp.Users.get_subscription(user.id)
    {:ok, %{user: user, subscription: sub}}
  end
end
|> Skuld.Query.with_executor(MyApp.Users, MyApp.UserExecutor)
|> FiberPool.with_handler()
|> Comp.run!()

Even though get_subscription depends on user.id, any other get_user calls in the block that are independent will be batched together.

Caching

Within-batch deduplication via Skuld.Query.Cache:

computation
|> Skuld.Query.with_cached_executor(MyApp.Users, MyApp.UserExecutor)
|> FiberPool.with_handler()

If the same get_user("123") is called twice in a batch, the second call hits the cache instead of the executor.

FunctionPurpose
deffetchDeclare a fetch operation
query do blockExpress data dependencies, auto-batch independent fetches
with_executor/2,3Wire a contract to an executor
with_cached_executor/2,3Wire with within-batch caching
with_cached_executors/2Wire multiple contracts

< Repo | Index | Hexagonal Architecture >