Packages

A Gleam actor registry that manages actors by key, with fast ETS lookups and automatic cleanup

Current section

Files

Jump to
reki src reki.erl
Raw

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, supervised/1, do_start/4, get_pid/1, new/0, lookup/2, lookup_or_start_with_timeout/4, lookup_or_start/3]).
-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(FLV, FLW) :: {registry,
gleam@erlang@atom:atom_(),
gleam@erlang@atom:atom_()} |
{gleam_phantom, FLV, FLW}.
-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(FLX, FLY)) -> {ok,
gleam@otp@actor:started(registry(FLX, FLY))} |
{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", 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(FMK, FML)) -> gleam@otp@supervision:child_specification(registry(FMK, FML)).
supervised(Registry) ->
gleam@otp@supervision:supervisor(fun() -> start(Registry) end).
-file("src/reki.gleam", 131).
?DOC(false).
-spec do_start(
registry(FNT, FNU),
FNT,
fun((FNT) -> {ok,
gleam@otp@actor:started(gleam@erlang@process:subject(FNU))} |
{error, gleam@otp@actor:start_error()}),
integer()
) -> {ok, gleam@erlang@process:subject(FNU)} |
{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", 142).
?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)).
-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", 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(FNC, FND), FNC) -> gleam@option:option(gleam@erlang@process:subject(FND)).
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(FNI, FNJ),
FNI,
fun((FNI) -> {ok,
gleam@otp@actor:started(gleam@erlang@process:subject(FNJ))} |
{error, gleam@otp@actor:start_error()}),
integer()
) -> {ok, gleam@erlang@process:subject(FNJ)} |
{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(FMR, FMS),
FMR,
fun((FMR) -> {ok,
gleam@otp@actor:started(gleam@erlang@process:subject(FMS))} |
{error, gleam@otp@actor:start_error()})
) -> {ok, gleam@erlang@process:subject(FMS)} |
{error, gleam@otp@actor:start_error()}.
lookup_or_start(Registry, Key, Start_fn) ->
lookup_or_start_with_timeout(Registry, Key, Start_fn, 5000).