Current section
Files
Jump to
Current section
Files
src/reki.erl
-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)).