Finch.Pool.Strategy behaviour (Finch v0.22.0)

View Source

Behaviour for selecting a pool worker when multiple workers are registered under the same pool key.

When Finch is configured with count: N, N pool workers register under the same key in a :duplicate Registry. The strategy selects one worker per request. The strategy callback and its state are passed in request opts (e.g. pool_strategy: {MyStrategy, state}); the caller owns and manages strategy state.

Using a built-in strategy

# Round-robin (state is an atomics counter)
counter = Finch.Pool.Strategy.RoundRobin.new()
Finch.request(req, MyFinch, pool_strategy: {Finch.Pool.Strategy.RoundRobin, counter})

For performance-critical paths, pass the strategy function directly to avoid dynamic module dispatch:

Finch.request(req, MyFinch, pool_strategy: {&Finch.Pool.Strategy.RoundRobin.select/2, counter})

# Hash-based (same key always maps to same worker; useful for connection affinity)
Finch.request(req, MyFinch, pool_strategy: {Finch.Pool.Strategy.Hash, team_id})

Built-in strategies: Finch.Pool.Strategy.Random, Finch.Pool.Strategy.RoundRobin, Finch.Pool.Strategy.Hash.

Custom strategy

Implement this behaviour and pass your module and state in request opts:

defmodule MyApp.LeastBusy do
  @behaviour Finch.Pool.Strategy

  @impl true
  def select(entries, _) do
    Enum.min_by(entries, fn {pid, _mod} ->
      case Process.info(pid, :message_queue_len) do
        {:message_queue_len, len} -> len
        nil -> :infinity
      end
    end)
  end
end

Finch.request(req, MyFinch, pool_strategy: MyApp.LeastBusy)

Strategies may implement the optional new/0 or new/1 callbacks to create state; the caller can also construct state themselves. Store state wherever makes sense (process state, :persistent_term, Application env, etc.).

Summary

Callbacks

Optional. Returns initial state for strategies that need no argument (e.g. Random, RoundRobin).

Optional. Returns initial state for strategies that take a key or option (e.g. Hash).

Selects one pool entry from the non-empty list of registered workers.

Types

pool_entry()

@type pool_entry() :: {pid(), module()}

Callbacks

new()

(optional)
@callback new() :: term()

Optional. Returns initial state for strategies that need no argument (e.g. Random, RoundRobin).

new(term)

(optional)
@callback new(term()) :: term()

Optional. Returns initial state for strategies that take a key or option (e.g. Hash).

select(entries, state)

@callback select(entries :: [pool_entry(), ...], state :: term()) :: pool_entry()

Selects one pool entry from the non-empty list of registered workers.

Called by Finch when multiple workers exist for the pool. The same state is passed for each call; the caller is responsible for any mutable state (e.g. atomics).