Process-wide registry of shared RepoHandles keyed by URL.
Solves Chris's "scenario 3" from the design review: 100 LiveView sessions searching the same repository should share ONE cache, not each clone the repo independently.
First caller to get_or_start/2 for a given URL pays the
clone + prefetch cost. Subsequent callers for the same URL
receive the same handle — the cache is warm.
API
# Atomic get-or-start. Blocks until the repo is cloned and
# the handle is ready. Returns the existing handle on
# subsequent calls for the same URL.
{:ok, handle} = Exgit.RepoRegistry.get_or_start(url)
# Check without starting.
case Exgit.RepoRegistry.lookup(url) do
{:ok, handle} -> handle
:error -> nil
end
# Stop and remove a handle.
:ok = Exgit.RepoRegistry.stop(url)
# Introspection.
Exgit.RepoRegistry.count()
Exgit.RepoRegistry.list()Concurrency
Concurrent calls to get_or_start/2 for the SAME URL serialize
at the registry GenServer; only one clone happens. Concurrent
calls for DIFFERENT URLs do NOT block each other in any
user-visible way (each hits a fast Registry.lookup; the slow
path serializes per-URL via a short-lived Mutex-equivalent
check inside the server).
Options
Per-URL options supplied to get_or_start/2 (e.g. :lazy,
:filter) are applied by the FIRST caller for that URL.
Subsequent callers' options are ignored (with a telemetry
event when they differ). Consumers who need different per-user
configs should use Exgit.clone/2 directly without the
registry.
Lifecycle
Handles started by the registry are linked to the registry
process. If the registry dies, all handles die. If a handle
dies (crash, explicit stop), the registry removes it from its
map and the next get_or_start/2 will start a fresh clone.
Start the registry as a named, singleton GenServer (typically
under the consumer's supervision tree). If not started, all
API calls error with :not_started.
Summary
Functions
Returns a specification to start this module under a supervisor.
Number of active handles.
Get an existing handle or start a fresh one.
List all URLs currently in the registry.
Lookup without starting. Returns {:ok, handle} or :error.
Start the RepoRegistry as a supervised GenServer.
Stop the handle for url and remove it from the registry.
Returns :ok whether or not the URL was registered.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec count() :: non_neg_integer()
Number of active handles.
@spec get_or_start( String.t(), keyword() ) :: {:ok, Exgit.RepoHandle.t()} | {:error, term()}
Get an existing handle or start a fresh one.
Blocks until the clone completes on the first call for a URL. Subsequent calls return immediately.
Options
Forwarded to Exgit.clone/2 on the first call for a given URL.
Defaults: lazy: true.
Errors
{:error, :not_started}— registry GenServer isn't running{:error, reason}— clone failed on the first call
@spec list() :: [String.t()]
List all URLs currently in the registry.
@spec lookup(String.t()) :: {:ok, Exgit.RepoHandle.t()} | :error
Lookup without starting. Returns {:ok, handle} or :error.
@spec start_link(keyword()) :: GenServer.on_start()
Start the RepoRegistry as a supervised GenServer.
Usually called by the consumer's supervision tree — the
library's own Exgit.Application does NOT start it, because
consumers who don't need cross-process sharing shouldn't pay
for a registry process they won't use.
Options are forwarded to GenServer.start_link/3.
@spec stop(String.t()) :: :ok
Stop the handle for url and remove it from the registry.
Returns :ok whether or not the URL was registered.