Current section

Files

Jump to
distribute src distribute.erl
Raw

src/distribute.erl

-module(distribute).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/distribute.gleam").
-export([version/0, configure/1, get_config/0, config_error_to_string/1, start_node/2, connect/1, nodes/0, self_node/0, is_distributed/0, has_peers/0, health/0, node_start_error_to_string/1, connect_error_to_string/1, start_monitor/0, subscribe/2, unsubscribe/2, install_telemetry/1, named/2, new_subject/1, register/2, lookup/1, unregister/1, unregister_typed/1, register_error_to_string/1, unregister_error_to_string/1, lookup_error_to_string/1, actor_start_error_to_string/1, start_registered_error_to_string/1, start_actor/3, start_actor_with_timeout/4, start_actor_observed/4, start_registered/3, start_registered_with_timeout/4, start_supervised/3, start_supervised_with_timeout/4, pool/4, pool_with_timeout/5, child_spec/3, child_spec_with_timeout/4, send/2, reply/3, 'receive'/1, receive_with_timeout/2, call/3, call_with_timeout/4, call_isolated/3, call_isolated_with_timeout/4, send_error_to_string/1, call_error_to_string/1, receive_error_to_string/1, encode_error_to_string/1, decode_error_to_string/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" `distribute`: Typed messaging for the BEAM.\n"
"\n"
" This library provides a safety layer over Erlang's native distribution.\n"
" It focuses on hardening the **Node Boundary**: the physical edge where\n"
" typed Gleam values become raw binary terms and vice versa.\n"
"\n"
" ## The Typed Boundary Concept\n"
"\n"
" In standard Erlang distribution, messages cross the wire as raw terms\n"
" without type information. `distribute` forces you to define a protocol\n"
" using `TypedName` and `Codec` before any data is sent.\n"
"\n"
" 1. **At the Sender**: messages are encoded, checked against payload\n"
" limits, and transmitted via BEAM distribution.\n"
" 2. **At the Receiver**: binaries have their size verified *before*\n"
" decoding. If they exceed limits, they are rejected. If they are\n"
" valid, they are decoded into typed Gleam values.\n"
"\n"
" This architecture ensures that an actor's mailbox is never flooded\n"
" with unparseable or oversized data, and the compiler can prevent\n"
" protocol mismatches at the call site.\n"
"\n"
" ## Core Philosophy\n"
"\n"
" - **Fail-Fast over Retry**: every failure (Timeout, TargetDown,\n"
" DecodeError) is surfaced immediately. Managing retries and circuit\n"
" breaking is left to the application layer.\n"
" - **Memory First**: payload limits are enforced at the I/O boundary\n"
" to protect nodes from OOM attacks or bugs.\n"
" - **OTP Native**: every part of the library (supervisors, monitors,\n"
" subjects) follows standard BEAM OTP patterns.\n"
" - **Zero Cost Read**: configuration is stored in Erlang's\n"
" `persistent_term` for O(1) reads with zero heap allocation.\n"
"\n"
" ## Typical Usage\n"
"\n"
" ```gleam\n"
" // 1. Configure once at application startup\n"
" let assert Ok(Nil) = distribute.configure(config.default())\n"
"\n"
" // 2. Define a protocol (Name + Codec)\n"
" let counter = distribute.named(\"counter\", codec.int())\n"
"\n"
" // 3. Start a globally registered, supervised singleton\n"
" let assert Ok(pid) = distribute.start_supervised(counter, 0, handler)\n"
"\n"
" // 4. Call from any node. Monitor-based TargetDown detection,\n"
" default timeout from config\n"
" let assert Ok(val) = distribute.call(gs, Get, codec.int_decoder())\n"
" ```\n"
"\n"
" ## Default vs explicit timeout\n"
"\n"
" Every long-running operation has two shapes:\n"
"\n"
" - The **default form** reads from `config.get()`. This is the path\n"
" you want 90% of the time. A single `configure(...)` at boot tunes\n"
" the whole surface (`call`, `receive`, `start_actor`,\n"
" `start_registered`, `start_supervised`, `pool`, `child_spec`,\n"
" `call_isolated`).\n"
" - The **`_with_timeout` form** is the explicit override. Pick it\n"
" only when the request has a hard deadline that diverges from\n"
" `config.default_*`.\n"
"\n"
" ## Error handling without extra imports\n"
"\n"
" Every public error type (and its `*_to_string` formatter) is\n"
" re-exported here. Pattern-matching on `CallError`, `RegisterError`,\n"
" `LookupError`, etc., requires only `import distribute`.\n"
"\n"
" ## Reply-Subject helpers live in `distribute/receiver`\n"
"\n"
" Handlers that answer a `call` typically need `receiver.receive_typed`\n"
" or `receiver.selecting_typed` to operate on the raw reply-Subject.\n"
" These are intra-handler primitives: import the module directly.\n"
"\n"
" ```gleam\n"
" import distribute/receiver\n"
" case receiver.receive_typed(reply_to, codec.int_decoder(), 1000) {\n"
" Ok(value) -> ...\n"
" Error(receiver.ReceiveTimeout) -> ...\n"
" Error(receiver.DecodeError(_)) -> ...\n"
" }\n"
" ```\n"
"\n"
" ## API surface, by concern\n"
"\n"
" - **Boot & config**: `configure`, `get_config`, `version`, `Config`,\n"
" `ConfigError`, `config_error_to_string`.\n"
" - **Node lifecycle**: `start_node`, `connect`, `nodes`, `self_node`,\n"
" `is_distributed`, `has_peers`, `health`, `ClusterHealth`,\n"
" `NodeStartError`, `ConnectError`,\n"
" `node_start_error_to_string`, `connect_error_to_string`.\n"
" - **Cluster events**: `start_monitor`, `subscribe`, `unsubscribe`,\n"
" `ClusterEvent`, `MonitorMessage`.\n"
" - **Protocol**: `named`, `new_subject`, `register`, `lookup`,\n"
" `unregister`, `unregister_typed`, `TypedName`, `GlobalSubject`,\n"
" `RegisterError`, `UnregisterError`, `LookupError`,\n"
" `register_error_to_string`, `unregister_error_to_string`,\n"
" `lookup_error_to_string`.\n"
" - **Actor lifecycle**: `start_actor` / `start_actor_with_timeout` /\n"
" `start_actor_observed`, `start_registered` /\n"
" `start_registered_with_timeout`, `start_supervised` /\n"
" `start_supervised_with_timeout`, `pool` / `pool_with_timeout`,\n"
" `child_spec` / `child_spec_with_timeout`, `HandlerStep`,\n"
" `ActorStartError`, `StartRegisteredError`,\n"
" `actor_start_error_to_string`, `start_registered_error_to_string`.\n"
" - **Messaging**: `send`, `reply`, `receive` / `receive_with_timeout`,\n"
" `call` / `call_with_timeout`, `call_isolated` /\n"
" `call_isolated_with_timeout`, `SendError`, `CallError`,\n"
" `ReceiveError`, `EncodeError`, `DecodeError`,\n"
" `send_error_to_string`, `call_error_to_string`,\n"
" `receive_error_to_string`, `encode_error_to_string`,\n"
" `decode_error_to_string`.\n"
"\n"
" Most users only need `import distribute`. Low-level modules\n"
" (`distribute/actor`, `distribute/global`, `distribute/registry`,\n"
" `distribute/codec`, `distribute/receiver`, `distribute/cluster`,\n"
" `distribute/cluster_monitor`, `distribute/codec/composite`,\n"
" `distribute/codec/tagged`, `distribute/config`) remain available for\n"
" advanced cases not covered by the facade. e.g. `whereis`,\n"
" `register_pid`, `register_typed`, `from_pid`, `from_subject`,\n"
" `lookup_with_timeout`, `lookup_async`, `start_registered_observed`,\n"
" `receive_typed`, `selecting_typed`.\n"
).
-file("src/distribute.gleam", 145).
?DOC(
" Library version, hardcoded. Must stay in sync with `gleam.toml`\n"
" at release time. Gleam has no compile-time API to read the\n"
" manifest, so this is a release-time discipline, not a runtime\n"
" invariant.\n"
).
-spec version() -> binary().
version() ->
<<"4.0.0"/utf8>>.
-file("src/distribute.gleam", 156).
?DOC(" Set global runtime configuration. Call once at application startup.\n").
-spec configure(distribute@config:config()) -> {ok, nil} |
{error, distribute@config:config_error()}.
configure(Cfg) ->
distribute@config:configure(Cfg).
-file("src/distribute.gleam", 161).
?DOC(" Read the current global configuration (or defaults if never configured).\n").
-spec get_config() -> distribute@config:config().
get_config() ->
distribute@config:get().
-file("src/distribute.gleam", 165).
-spec config_error_to_string(distribute@config:config_error()) -> binary().
config_error_to_string(Err) ->
distribute@config:config_error_to_string(Err).
-file("src/distribute.gleam", 184).
?DOC(
" Start a distributed BEAM node.\n"
"\n"
" `name` must contain `@` (e.g. `\"myapp@127.0.0.1\"`).\n"
" `cookie` must be `[a-zA-Z0-9_-]+` and 1..255 bytes (validated by FFI).\n"
).
-spec start_node(binary(), binary()) -> {ok, nil} |
{error, distribute@cluster:start_error()}.
start_node(Name, Cookie) ->
distribute@cluster:start_node(Name, Cookie).
-file("src/distribute.gleam", 188).
-spec connect(binary()) -> {ok, nil} |
{error, distribute@cluster:connect_error()}.
connect(Node) ->
distribute@cluster:connect(Node).
-file("src/distribute.gleam", 192).
-spec nodes() -> list(binary()).
nodes() ->
distribute@cluster:nodes().
-file("src/distribute.gleam", 196).
-spec self_node() -> binary().
self_node() ->
distribute@cluster:self_node().
-file("src/distribute.gleam", 201).
?DOC(" Whether this node is running BEAM distribution (via `erlang:is_alive/0`).\n").
-spec is_distributed() -> boolean().
is_distributed() ->
distribute@cluster:is_distributed().
-file("src/distribute.gleam", 208).
?DOC(
" Whether this node has at least one connected peer. *Not* a health\n"
" check. A single-node deployment is operationally fine and will\n"
" return `False` here. Use this only to gate cluster-wide operations.\n"
).
-spec has_peers() -> boolean().
has_peers() ->
distribute@cluster:has_peers().
-file("src/distribute.gleam", 213).
?DOC(" Full cluster health snapshot with parallel pings.\n").
-spec health() -> distribute@cluster:cluster_health().
health() ->
distribute@cluster:health().
-file("src/distribute.gleam", 217).
-spec node_start_error_to_string(distribute@cluster:start_error()) -> binary().
node_start_error_to_string(Err) ->
distribute@cluster:start_error_to_string(Err).
-file("src/distribute.gleam", 221).
-spec connect_error_to_string(distribute@cluster:connect_error()) -> binary().
connect_error_to_string(Err) ->
distribute@cluster:connect_error_to_string(Err).
-file("src/distribute.gleam", 235).
?DOC(
" Start the cluster monitor actor. It listens for Erlang node events\n"
" and broadcasts them to all Gleam subscribers.\n"
).
-spec start_monitor() -> {ok,
gleam@erlang@process:subject(distribute@cluster_monitor:message())} |
{error, gleam@otp@actor:start_error()}.
start_monitor() ->
distribute@cluster:start_monitor().
-file("src/distribute.gleam", 242).
-spec subscribe(
gleam@erlang@process:subject(distribute@cluster_monitor:message()),
gleam@erlang@process:subject(distribute@cluster_monitor:cluster_event())
) -> nil.
subscribe(Monitor, Listener) ->
distribute@cluster:subscribe(Monitor, Listener).
-file("src/distribute.gleam", 249).
-spec unsubscribe(
gleam@erlang@process:subject(distribute@cluster_monitor:message()),
gleam@erlang@process:subject(distribute@cluster_monitor:cluster_event())
) -> nil.
unsubscribe(Monitor, Listener) ->
distribute@cluster:unsubscribe(Monitor, Listener).
-file("src/distribute.gleam", 268).
?DOC(
" Install (or replace) the global telemetry sink for observability.\n"
" This single opt-in sink receives all load-bearing events from the library\n"
" (registry, atom budget, payload limits, codec failures, timeouts).\n"
" See `distribute/telemetry` for the full semantics and event structure.\n"
).
-spec install_telemetry(fun((distribute@telemetry:event()) -> nil)) -> nil.
install_telemetry(Sink) ->
distribute@telemetry:install(Sink).
-file("src/distribute.gleam", 290).
?DOC(
" Create a `TypedName` from a bundled `Codec`.\n"
"\n"
" ```gleam\n"
" let counter = distribute.named(\"counter\", codec.int())\n"
" ```\n"
).
-spec named(binary(), distribute@codec:codec(HXO)) -> distribute@registry:typed_name(HXO).
named(Name, C) ->
distribute@registry:named(Name, C).
-file("src/distribute.gleam", 297).
?DOC(
" Create a new `GlobalSubject` owned by the current process, from a\n"
" bundled `Codec`. For separate encoder/decoder, drop down to\n"
" `global.new` directly.\n"
).
-spec new_subject(distribute@codec:codec(HXR)) -> distribute@global:global_subject(HXR).
new_subject(C) ->
distribute@global:new(erlang:element(2, C), erlang:element(3, C)).
-file("src/distribute.gleam", 325).
?DOC(
" Register a `GlobalSubject` under its `TypedName`. The typed pair is\n"
" the recommended path; for raw-PID registration import\n"
" `distribute/registry` and call `registry.register/2` directly.\n"
).
-spec register(
distribute@registry:typed_name(HXU),
distribute@global:global_subject(HXU)
) -> {ok, nil} | {error, distribute@registry:register_error()}.
register(Typed_name, Subject) ->
distribute@registry:register_global(Typed_name, Subject).
-file("src/distribute.gleam", 335).
?DOC(
" Look up a `GlobalSubject` by its `TypedName`. For polling variants\n"
" (blocking and async) see `registry.lookup_with_timeout` /\n"
" `registry.lookup_async`.\n"
).
-spec lookup(distribute@registry:typed_name(HXZ)) -> {ok,
distribute@global:global_subject(HXZ)} |
{error, nil}.
lookup(Typed_name) ->
distribute@registry:lookup(Typed_name).
-file("src/distribute.gleam", 341).
?DOC(
" Unregister a globally registered name. Idempotent cleanup paths can\n"
" `let _ = unregister(name)`.\n"
).
-spec unregister(binary()) -> {ok, nil} |
{error, distribute@registry:unregister_error()}.
unregister(Name) ->
distribute@registry:unregister(Name).
-file("src/distribute.gleam", 348).
?DOC(
" Type-safe sibling of `unregister/1`: pulls the name string from the\n"
" `TypedName` the caller already holds. Recommended for graceful\n"
" shutdown paths so the cleanup site never has to hardcode the name.\n"
).
-spec unregister_typed(distribute@registry:typed_name(any())) -> {ok, nil} |
{error, distribute@registry:unregister_error()}.
unregister_typed(Typed_name) ->
distribute@registry:unregister_typed(Typed_name).
-file("src/distribute.gleam", 354).
-spec register_error_to_string(distribute@registry:register_error()) -> binary().
register_error_to_string(Err) ->
distribute@registry:register_error_to_string(Err).
-file("src/distribute.gleam", 358).
-spec unregister_error_to_string(distribute@registry:unregister_error()) -> binary().
unregister_error_to_string(Err) ->
distribute@registry:unregister_error_to_string(Err).
-file("src/distribute.gleam", 362).
-spec lookup_error_to_string(distribute@registry:lookup_error()) -> binary().
lookup_error_to_string(Err) ->
distribute@registry:lookup_error_to_string(Err).
-file("src/distribute.gleam", 375).
?DOC(" Render a `gleam_otp/actor.StartError` as a human-readable string.\n").
-spec actor_start_error_to_string(gleam@otp@actor:start_error()) -> binary().
actor_start_error_to_string(Err) ->
case Err of
init_timeout ->
<<"Actor init timed out"/utf8>>;
{init_failed, Reason} ->
<<"Actor init failed: "/utf8, Reason/binary>>;
{init_exited, _} ->
<<"Actor init process exited"/utf8>>
end.
-file("src/distribute.gleam", 383).
-spec start_registered_error_to_string(
distribute@actor:start_registered_error()
) -> binary().
start_registered_error_to_string(Err) ->
distribute@actor:start_registered_error_to_string(Err).
-file("src/distribute.gleam", 389).
?DOC(
" Start a named actor using `config.get().default_init_timeout_ms`.\n"
" Use `start_actor_with_timeout/4` for an explicit timeout.\n"
).
-spec start_actor(
distribute@registry:typed_name(HYK),
HYM,
fun((HYK, HYM) -> distribute@receiver:handler_step(HYM))
) -> {ok, distribute@global:global_subject(HYK)} |
{error, gleam@otp@actor:start_error()}.
start_actor(Typed_name, Initial_state, Handler) ->
distribute@actor:start_default(Typed_name, Initial_state, Handler).
-file("src/distribute.gleam", 398).
?DOC(" Start a named actor with an explicit init timeout.\n").
-spec start_actor_with_timeout(
distribute@registry:typed_name(HYR),
HYT,
fun((HYR, HYT) -> distribute@receiver:handler_step(HYT)),
integer()
) -> {ok, distribute@global:global_subject(HYR)} |
{error, gleam@otp@actor:start_error()}.
start_actor_with_timeout(Typed_name, Initial_state, Handler, Init_timeout_ms) ->
distribute@actor:start(Typed_name, Initial_state, Handler, Init_timeout_ms).
-file("src/distribute.gleam", 412).
?DOC(
" Start a named actor with a decode-error callback. Useful for\n"
" logging or metering malformed messages across nodes (e.g. during\n"
" rolling deploys with mismatched codec versions). Uses\n"
" `config.get().default_init_timeout_ms`. If you need a custom\n"
" init timeout, drop down to `actor.start_observed` directly.\n"
).
-spec start_actor_observed(
distribute@registry:typed_name(HYY),
HZA,
fun((HYY, HZA) -> distribute@receiver:handler_step(HZA)),
fun((distribute@codec:decode_error()) -> nil)
) -> {ok, distribute@global:global_subject(HYY)} |
{error, gleam@otp@actor:start_error()}.
start_actor_observed(Typed_name, Initial_state, Handler, On_decode_error) ->
distribute@actor:start_observed(
Typed_name,
Initial_state,
Handler,
erlang:element(3, distribute@config:get()),
On_decode_error
).
-file("src/distribute.gleam", 431).
?DOC(
" Start an actor and register it globally in one step. Uses\n"
" `config.get().default_init_timeout_ms`. Use\n"
" `start_registered_with_timeout/4` for an explicit timeout, or\n"
" `actor.start_registered_observed` for the decode-error hook variant.\n"
).
-spec start_registered(
distribute@registry:typed_name(HZF),
HZH,
fun((HZF, HZH) -> distribute@receiver:handler_step(HZH))
) -> {ok, distribute@global:global_subject(HZF)} |
{error, distribute@actor:start_registered_error()}.
start_registered(Typed_name, Initial_state, Handler) ->
distribute@actor:start_registered_default(
Typed_name,
Initial_state,
Handler
).
-file("src/distribute.gleam", 440).
?DOC(" Like `start_registered`, with an explicit init timeout.\n").
-spec start_registered_with_timeout(
distribute@registry:typed_name(HZM),
HZO,
fun((HZM, HZO) -> distribute@receiver:handler_step(HZO)),
integer()
) -> {ok, distribute@global:global_subject(HZM)} |
{error, distribute@actor:start_registered_error()}.
start_registered_with_timeout(
Typed_name,
Initial_state,
Handler,
Init_timeout_ms
) ->
distribute@actor:start_registered(
Typed_name,
Initial_state,
Handler,
Init_timeout_ms
).
-file("src/distribute.gleam", 455).
?DOC(" Start a supervised actor using `config.get().default_init_timeout_ms`.\n").
-spec start_supervised(
distribute@registry:typed_name(HZT),
HZV,
fun((HZT, HZV) -> distribute@receiver:handler_step(HZV))
) -> {ok, gleam@erlang@process:pid_()} | {error, gleam@otp@actor:start_error()}.
start_supervised(Typed_name, Initial_state, Handler) ->
distribute@actor:start_supervised_default(
Typed_name,
Initial_state,
Handler
).
-file("src/distribute.gleam", 464).
?DOC(" Like `start_supervised`, with an explicit init timeout.\n").
-spec start_supervised_with_timeout(
distribute@registry:typed_name(HZZ),
IAB,
fun((HZZ, IAB) -> distribute@receiver:handler_step(IAB)),
integer()
) -> {ok, gleam@erlang@process:pid_()} | {error, gleam@otp@actor:start_error()}.
start_supervised_with_timeout(
Typed_name,
Initial_state,
Handler,
Init_timeout_ms
) ->
distribute@actor:start_supervised(
Typed_name,
Initial_state,
Handler,
Init_timeout_ms
).
-file("src/distribute.gleam", 479).
?DOC(" Start N supervised actors using `config.get().default_init_timeout_ms`.\n").
-spec pool(
distribute@registry:typed_name(IAF),
integer(),
IAH,
fun((IAF, IAH) -> distribute@receiver:handler_step(IAH))
) -> {ok, gleam@erlang@process:pid_()} | {error, gleam@otp@actor:start_error()}.
pool(Typed_name, Size, Initial_state, Handler) ->
distribute@actor:pool_default(Typed_name, Size, Initial_state, Handler).
-file("src/distribute.gleam", 489).
?DOC(" Like `pool`, with an explicit init timeout.\n").
-spec pool_with_timeout(
distribute@registry:typed_name(IAL),
integer(),
IAN,
fun((IAL, IAN) -> distribute@receiver:handler_step(IAN)),
integer()
) -> {ok, gleam@erlang@process:pid_()} | {error, gleam@otp@actor:start_error()}.
pool_with_timeout(Typed_name, Size, Initial_state, Handler, Init_timeout_ms) ->
distribute@actor:pool(
Typed_name,
Size,
Initial_state,
Handler,
Init_timeout_ms
).
-file("src/distribute.gleam", 500).
?DOC(" OTP child spec using `config.get().default_init_timeout_ms`.\n").
-spec child_spec(
distribute@registry:typed_name(IAR),
IAT,
fun((IAR, IAT) -> distribute@receiver:handler_step(IAT))
) -> gleam@otp@supervision:child_specification(distribute@global:global_subject(IAR)).
child_spec(Typed_name, Initial_state, Handler) ->
distribute@actor:child_spec_default(Typed_name, Initial_state, Handler).
-file("src/distribute.gleam", 509).
?DOC(" Like `child_spec`, with an explicit init timeout.\n").
-spec child_spec_with_timeout(
distribute@registry:typed_name(IAX),
IAZ,
fun((IAX, IAZ) -> distribute@receiver:handler_step(IAZ)),
integer()
) -> gleam@otp@supervision:child_specification(distribute@global:global_subject(IAX)).
child_spec_with_timeout(Typed_name, Initial_state, Handler, Init_timeout_ms) ->
distribute@actor:child_spec(
Typed_name,
Initial_state,
Handler,
Init_timeout_ms
).
-file("src/distribute.gleam", 535).
-spec send(distribute@global:global_subject(IBD), IBD) -> {ok, nil} |
{error, distribute@global:send_error()}.
send(Subject, Message) ->
distribute@global:send(Subject, Message).
-file("src/distribute.gleam", 543).
?DOC(" Send a response through a reply subject. Used by handlers to answer a `call`.\n").
-spec reply(
gleam@erlang@process:subject(bitstring()),
IBI,
fun((IBI) -> {ok, bitstring()} | {error, distribute@codec:encode_error()})
) -> {ok, nil} | {error, distribute@global:send_error()}.
reply(Reply_to, Response, Encoder) ->
distribute@global:reply(Reply_to, Response, Encoder).
-file("src/distribute.gleam", 553).
?DOC(
" Receive a typed message using `config.get().default_call_timeout_ms`.\n"
" Use `receive_with_timeout/2` for an explicit timeout.\n"
).
-spec 'receive'(distribute@global:global_subject(IBM)) -> {ok, IBM} |
{error, distribute@codec:decode_error()}.
'receive'(Subject) ->
distribute@global:receive_default(Subject).
-file("src/distribute.gleam", 558).
?DOC(" Like `receive`, with an explicit timeout.\n").
-spec receive_with_timeout(distribute@global:global_subject(IBQ), integer()) -> {ok,
IBQ} |
{error, distribute@codec:decode_error()}.
receive_with_timeout(Subject, Timeout_ms) ->
distribute@global:'receive'(Subject, Timeout_ms).
-file("src/distribute.gleam", 588).
?DOC(
" Synchronous request/response with monitor-based `TargetDown` detection.\n"
" Uses `config.get().default_call_timeout_ms`. Use `call_with_timeout/4`\n"
" for an explicit timeout.\n"
"\n"
" ## Late-reply caveat: choose `call_isolated` for long-running callers\n"
"\n"
" `call` is the cheap default. It is **safe** for short-lived callers\n"
" (CLI tools, request handlers, scripts) whose process exits shortly\n"
" after the call returns: orphan late-replies die with the process.\n"
"\n"
" It is **not** the right choice for long-lived processes (OTP\n"
" actors, supervisors, manager loops) that issue many `call`s under\n"
" sustained timeouts. A reply that arrives *after* `call` returns\n"
" `Error(Timeout)` cannot be evicted from the caller's mailbox by\n"
" the BEAM (no `erlang:alias/0`-aware Subject layout in the current\n"
" `gleam_erlang`); selective receive scans every orphan on every\n"
" subsequent `process.receive`, and tens of thousands of orphans\n"
" quietly degrade the caller's throughput.\n"
"\n"
" For that shape, prefer `call_isolated/3`: it runs each call inside\n"
" a short-lived unlinked proxy process whose mailbox is reaped on\n"
" exit. See `global.call/4` and `docs/safety_and_limits.md` for the\n"
" full design rationale.\n"
).
-spec call(
distribute@global:global_subject(IBU),
fun((gleam@erlang@process:subject(bitstring())) -> IBU),
fun((bitstring()) -> {ok, IBX} | {error, distribute@codec:decode_error()})
) -> {ok, IBX} | {error, distribute@global:call_error()}.
call(Target, Make_request, Response_decoder) ->
distribute@global:call_default(Target, Make_request, Response_decoder).
-file("src/distribute.gleam", 598).
?DOC(
" Like `call`, with an explicit timeout. Inherits the same\n"
" late-reply caveat. See `call/3`.\n"
).
-spec call_with_timeout(
distribute@global:global_subject(ICB),
fun((gleam@erlang@process:subject(bitstring())) -> ICB),
fun((bitstring()) -> {ok, ICE} | {error, distribute@codec:decode_error()}),
integer()
) -> {ok, ICE} | {error, distribute@global:call_error()}.
call_with_timeout(Target, Make_request, Response_decoder, Timeout_ms) ->
distribute@global:call(Target, Make_request, Response_decoder, Timeout_ms).
-file("src/distribute.gleam", 614).
?DOC(
" Mailbox-safe variant of `call` using\n"
" `config.get().default_call_timeout_ms`. Each invocation runs inside\n"
" a short-lived unlinked proxy process so orphan late-replies die\n"
" with the proxy instead of polluting the caller's mailbox.\n"
" Recommended for long-running callers issuing many RPCs under\n"
" sustained timeouts. See `global.call_isolated` for the full design\n"
" rationale.\n"
).
-spec call_isolated(
distribute@global:global_subject(ICI),
fun((gleam@erlang@process:subject(bitstring())) -> ICI),
fun((bitstring()) -> {ok, ICL} | {error, distribute@codec:decode_error()})
) -> {ok, ICL} | {error, distribute@global:call_error()}.
call_isolated(Target, Make_request, Response_decoder) ->
distribute@global:call_isolated_default(
Target,
Make_request,
Response_decoder
).
-file("src/distribute.gleam", 623).
?DOC(" Like `call_isolated`, with an explicit timeout.\n").
-spec call_isolated_with_timeout(
distribute@global:global_subject(ICP),
fun((gleam@erlang@process:subject(bitstring())) -> ICP),
fun((bitstring()) -> {ok, ICS} | {error, distribute@codec:decode_error()}),
integer()
) -> {ok, ICS} | {error, distribute@global:call_error()}.
call_isolated_with_timeout(Target, Make_request, Response_decoder, Timeout_ms) ->
distribute@global:call_isolated(
Target,
Make_request,
Response_decoder,
Timeout_ms
).
-file("src/distribute.gleam", 632).
-spec send_error_to_string(distribute@global:send_error()) -> binary().
send_error_to_string(Err) ->
distribute@global:send_error_to_string(Err).
-file("src/distribute.gleam", 636).
-spec call_error_to_string(distribute@global:call_error()) -> binary().
call_error_to_string(Err) ->
distribute@global:call_error_to_string(Err).
-file("src/distribute.gleam", 640).
-spec receive_error_to_string(distribute@receiver:receive_error()) -> binary().
receive_error_to_string(Err) ->
distribute@receiver:receive_error_to_string(Err).
-file("src/distribute.gleam", 644).
-spec encode_error_to_string(distribute@codec:encode_error()) -> binary().
encode_error_to_string(Err) ->
distribute@codec:encode_error_to_string(Err).
-file("src/distribute.gleam", 648).
-spec decode_error_to_string(distribute@codec:decode_error()) -> binary().
decode_error_to_string(Err) ->
distribute@codec:decode_error_to_string(Err).