Current section
Files
Jump to
Current section
Files
lib/sagents/process_registry.ex
defmodule Sagents.ProcessRegistry do
@moduledoc """
Abstraction over process registry implementations.
Supports two backends:
- `:local` — Elixir's built-in `Registry` (single-node, zero extra deps)
- `:horde` — `Horde.Registry` (distributed, requires the `:horde` dependency)
## Configuration
# config/config.exs (or runtime.exs)
# Single-node (default — no config needed)
config :sagents, :distribution, :local
# Distributed cluster
config :sagents, :distribution, :horde
When `:horde` is selected, Horde.Registry is started with `members: :auto`
so it automatically discovers other nodes in the Erlang cluster.
"""
@compile {:no_warn_undefined, [Horde.Registry, Sagents.Horde.RegistryImpl]}
@registry_name Sagents.Registry
# ---------------------------------------------------------------------------
# Startup
# ---------------------------------------------------------------------------
@doc """
Returns the child spec for the configured registry backend.
Used in `Sagents.Application` supervision tree.
"""
def child_spec(_opts) do
case distribution_type() do
:local ->
{Registry, keys: :unique, name: @registry_name}
:horde ->
assert_horde_available!()
# Use module-based implementation for dynamic config
Supervisor.child_spec(Sagents.Horde.RegistryImpl, shutdown: 15_000)
end
end
# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------
@doc """
Returns a `:via` tuple for registering or looking up a process by key.
## Examples
Sagents.ProcessRegistry.via_tuple({:agent_server, "agent-123"})
# => {:via, Registry, {Sagents.Registry, {:agent_server, "agent-123"}}}
# or
# => {:via, Horde.Registry, {Sagents.Registry, {:agent_server, "agent-123"}}}
"""
def via_tuple(key) do
{:via, registry_module(), {@registry_name, key}}
end
@doc """
Look up a process by key.
Returns `[{pid, value}]` if found, `[]` otherwise.
## Examples
[{pid, _}] = Sagents.ProcessRegistry.lookup({:agent_server, "agent-123"})
"""
def lookup(key) do
registry_module().lookup(@registry_name, key)
end
@doc """
Select processes matching a match specification.
The match spec format is the same as `Registry.select/2`.
## Examples
Sagents.ProcessRegistry.select([
{{{:agent_server, :"\$1"}, :_, :_}, [], [:"\$1"]}
])
"""
def select(match_spec) do
registry_module().select(@registry_name, match_spec)
end
@doc """
Returns the count of all registered entries.
"""
def count do
registry_module().count(@registry_name)
end
@doc """
Returns the keys for the given process `pid`.
## Examples
Sagents.ProcessRegistry.keys(pid)
# => [{:agent_supervisor, "agent-123"}]
"""
def keys(pid) do
registry_module().keys(@registry_name, pid)
end
@doc """
Returns the registry module (`Registry` or `Horde.Registry`).
"""
def registry_module do
case distribution_type() do
:local -> Registry
:horde -> Horde.Registry
end
end
@doc """
Returns the registry name atom (`Sagents.Registry`).
"""
def registry_name, do: @registry_name
# ---------------------------------------------------------------------------
# Internal
# ---------------------------------------------------------------------------
defp distribution_type do
Application.get_env(:sagents, :distribution, :local)
end
defp assert_horde_available! do
unless Code.ensure_loaded?(Horde.Registry) do
raise """
Sagents is configured to use Horde (config :sagents, :distribution, :horde)
but the :horde dependency is not available.
Add it to your mix.exs:
{:horde, "~> 0.10"}
"""
end
end
end