-module(reki). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/reki.gleam"). -export([start/1, new/0, supervised/1, do_start/4, lookup/2, lookup_or_start_with_timeout/4, lookup_or_start/3, to_list/1, get_pid/1]). -export_type([registry/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -opaque registry(FGB, FGC) :: {registry, gleam@erlang@atom:atom_(), gleam@erlang@atom:atom_()} | {gleam_phantom, FGB, FGC}. -file("src/reki.gleam", 47). ?DOC( " Start the registry. **You likely want to use the `supervised` function instead**,\n" " to add the registry to your supervision tree, but this may be useful in tests.\n" ). -spec start(registry(FGD, FGE)) -> {ok, gleam@otp@actor:started(registry(FGD, FGE))} | {error, gleam@otp@actor:start_error()}. start(Registry) -> _pipe = reki_server:start_link( erlang:element(2, Registry), erlang:element(3, Registry) ), gleam@result:map(_pipe, fun(Pid) -> {started, Pid, Registry} end). -file("src/reki.gleam", 62). ?DOC( " Create a registry. Call this at the start of your program before\n" " creating the supervision tree.\n" "\n" " **Important:** Each call creates unique atoms for the server name and\n" " ETS table name. Atoms are never garbage collected by the BEAM, so this\n" " function must only be called a fixed number of times (e.g. once per\n" " registry at app startup). Do not call `new()` dynamically in a loop\n" " or on each request.\n" ). -spec new() -> registry(any(), any()). new() -> {registry, reki_server:new_unique_atom(), reki_server:new_unique_atom()}. -file("src/reki.gleam", 70). ?DOC( " A specification for starting the registry under a supervisor.\n" "\n" " The registry declares itself as a supervisor in the child spec, since it\n" " manages child processes and implements the OTP supervisor protocol.\n" ). -spec supervised(registry(FGQ, FGR)) -> gleam@otp@supervision:child_specification(registry(FGQ, FGR)). supervised(Registry) -> gleam@otp@supervision:supervisor(fun() -> start(Registry) end). -file("src/reki.gleam", 131). ?DOC(false). -spec do_start( registry(FHZ, FIA), FHZ, fun((FHZ) -> {ok, gleam@otp@actor:started(gleam@erlang@process:subject(FIA))} | {error, gleam@otp@actor:start_error()}), integer() ) -> {ok, gleam@erlang@process:subject(FIA)} | {error, gleam@otp@actor:start_error()}. do_start(Registry, Key, Start_fn, Timeout) -> reki_server:start_child(erlang:element(2, Registry), Key, Start_fn, Timeout). -file("src/reki.gleam", 99). ?DOC( " Looks up an actor by key in the registry, without starting one if missing.\n" "\n" " Returns `None` if no actor exists for the given key. This is a direct ETS\n" " read, so it's fast but may return a stale Subject if a process just died.\n" "\n" " Use `lookup_or_start` if you want to start an actor when the key is missing.\n" ). -spec lookup(registry(FHI, FHJ), FHI) -> gleam@option:option(gleam@erlang@process:subject(FHJ)). lookup(Registry, Key) -> reki_server:lookup(erlang:element(3, Registry), Key). -file("src/reki.gleam", 107). ?DOC(false). -spec lookup_or_start_with_timeout( registry(FHO, FHP), FHO, fun((FHO) -> {ok, gleam@otp@actor:started(gleam@erlang@process:subject(FHP))} | {error, gleam@otp@actor:start_error()}), integer() ) -> {ok, gleam@erlang@process:subject(FHP)} | {error, gleam@otp@actor:start_error()}. lookup_or_start_with_timeout(Registry, Key, Start_fn, Timeout) -> case lookup(Registry, Key) of {some, Sub} -> {ok, Sub}; none -> do_start(Registry, Key, Start_fn, Timeout) end. -file("src/reki.gleam", 84). ?DOC( " Looks up an actor by key, or starts one if it doesn't exist.\n" "\n" " This is the primary function for getting an actor from the registry. It\n" " first checks ETS for an existing entry (fast O(1) lookup), and only goes\n" " through the registry gen_server if the key isn't found.\n" "\n" " In rare cases, this may return a stale Subject if a process just died but\n" " reki hasn't processed the EXIT yet.\n" ). -spec lookup_or_start( registry(FGX, FGY), FGX, fun((FGX) -> {ok, gleam@otp@actor:started(gleam@erlang@process:subject(FGY))} | {error, gleam@otp@actor:start_error()}) ) -> {ok, gleam@erlang@process:subject(FGY)} | {error, gleam@otp@actor:start_error()}. lookup_or_start(Registry, Key, Start_fn) -> lookup_or_start_with_timeout(Registry, Key, Start_fn, 5000). -file("src/reki.gleam", 150). ?DOC( " List every `(key, subject)` pair currently in the registry, in no\n" " particular order. A direct ETS read (`ets:tab2list`), so it's cheap and\n" " doesn't serialise through the gen_server — and, like `lookup`, it may\n" " briefly include an entry for a process that just died but whose EXIT\n" " reki hasn't processed yet.\n" "\n" " Mirrors gen_registry's `to_list/1` (its `reduce/3` is `to_list |> fold`\n" " here): use it to sweep the registry (rebalancing, metrics, draining)\n" " without maintaining a parallel index of keys.\n" ). -spec to_list(registry(FIK, FIL)) -> list({FIK, gleam@erlang@process:subject(FIL)}). to_list(Registry) -> ets:tab2list(erlang:element(3, Registry)). -file("src/reki.gleam", 157). ?DOC(false). -spec get_pid(registry(any(), any())) -> {ok, gleam@erlang@process:pid_()} | {error, nil}. get_pid(Registry) -> reki_server:whereis_server(erlang:element(2, Registry)).