Exgit.RepoRegistry (exgit v0.1.0)

Copy Markdown View Source

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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

count()

@spec count() :: non_neg_integer()

Number of active handles.

get_or_start(url, clone_opts \\ [])

@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

list()

@spec list() :: [String.t()]

List all URLs currently in the registry.

lookup(url)

@spec lookup(String.t()) :: {:ok, Exgit.RepoHandle.t()} | :error

Lookup without starting. Returns {:ok, handle} or :error.

start_link(opts \\ [])

@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.

stop(url)

@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.