Packages

Typed distributed messaging for Gleam on the BEAM.

Retired package: Deprecated - The project needs to be redesigned around a much smaller and clearer core.

Current section

Files

Jump to
distribute src distribute@messaging.erl
Raw

src/distribute@messaging.erl

-module(distribute@messaging).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/distribute/messaging.gleam").
-export([classify_send_error_to_string/1, is_network_error/1, classify_send_error/2, send/2, send_global/2, send_batch/1, send_batch_strict/1]).
-export_type([send_error/0, dynamic_/0, pid_/0, batch_send_result/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type send_error() :: {name_not_found, binary()} |
process_not_alive |
{network_error, binary()} |
{invalid_message, binary()} |
{send_failed, binary()}.
-type dynamic_() :: any().
-type pid_() :: any().
-type batch_send_result() :: {batch_send_result,
integer(),
integer(),
integer(),
list(send_error())}.
-file("src/distribute/messaging.gleam", 59).
?DOC(" Convert SendError to string for logging\n").
-spec classify_send_error_to_string(send_error()) -> binary().
classify_send_error_to_string(Error) ->
case Error of
{name_not_found, Name} ->
<<"name_not_found: "/utf8, Name/binary>>;
process_not_alive ->
<<"process_not_alive"/utf8>>;
{network_error, Name@1} ->
<<"network_error: "/utf8, Name@1/binary>>;
{invalid_message, Name@2} ->
<<"invalid_message: "/utf8, Name@2/binary>>;
{send_failed, Reason} ->
<<"send_failed: "/utf8, Reason/binary>>
end.
-file("src/distribute/messaging.gleam", 70).
?DOC(" Check if error is network-related\n").
-spec is_network_error(binary()) -> boolean().
is_network_error(Reason) ->
(gleam_stdlib:contains_string(Reason, <<"network"/utf8>>) orelse gleam_stdlib:contains_string(
Reason,
<<"connection"/utf8>>
))
orelse gleam_stdlib:contains_string(Reason, <<"partition"/utf8>>).
-file("src/distribute/messaging.gleam", 45).
?DOC(" Classify error reason into structured SendError\n").
-spec classify_send_error(binary(), binary()) -> send_error().
classify_send_error(Reason, Name) ->
case Reason of
<<"not_found"/utf8>> ->
{name_not_found, Name};
<<"process_not_alive"/utf8>> ->
process_not_alive;
<<"invalid_message"/utf8>> ->
{invalid_message, Name};
_ ->
case is_network_error(Reason) of
true ->
{network_error, Name};
false ->
{send_failed, Reason}
end
end.
-file("src/distribute/messaging.gleam", 77).
?DOC(" Send a message to a process (local or remote).\n").
-spec send(pid_(), any()) -> nil.
send(Pid, Msg) ->
erlang:send(Pid, Msg),
nil.
-file("src/distribute/messaging.gleam", 84).
?DOC(
" Send a message to a globally registered name.\n"
" Returns Ok(Nil) if successful, Error if name not found or send failed.\n"
).
-spec send_global(binary(), any()) -> {ok, nil} | {error, send_error()}.
send_global(Name, Msg) ->
distribute@log:debug(
<<"Sending message to global name"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
Res = messaging_ffi:send_global(Name, Msg),
case messaging_ffi:is_ok_atom(Res) of
true ->
distribute@log:debug(
<<"Message sent successfully"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
{ok, nil};
false ->
case messaging_ffi:is_not_found(Res) of
true ->
Error = {name_not_found, Name},
distribute@log:warn(
<<"Global name not found"/utf8>>,
[{<<"name"/utf8>>, Name},
{<<"error"/utf8>>,
classify_send_error_to_string(Error)}]
),
{error, Error};
false ->
Error@1 = classify_send_error(
messaging_ffi:get_error_reason(Res),
Name
),
distribute@log:error(
<<"Failed to send message"/utf8>>,
[{<<"name"/utf8>>, Name},
{<<"error"/utf8>>,
classify_send_error_to_string(Error@1)}]
),
{error, Error@1}
end
end.
-file("src/distribute/messaging.gleam", 126).
?DOC(
" Send multiple messages in batch with error aggregation\n"
" Returns detailed results including all errors encountered\n"
).
-spec send_batch(list({binary(), any()})) -> batch_send_result().
send_batch(Messages) ->
distribute@log:debug(
<<"Sending batch of messages"/utf8>>,
[{<<"count"/utf8>>, gleam@string:inspect(erlang:length(Messages))}]
),
Results = gleam@list:map(
Messages,
fun(Msg_pair) ->
{Name, Msg} = Msg_pair,
send_global(Name, Msg)
end
),
Successes = begin
_pipe = gleam@list:filter(Results, fun(R) -> case R of
{ok, _} ->
true;
{error, _} ->
false
end end),
erlang:length(_pipe)
end,
Errors = gleam@list:fold(Results, [], fun(Acc, R@1) -> case R@1 of
{error, E} ->
[E | Acc];
{ok, _} ->
Acc
end end),
Total = erlang:length(Messages),
distribute@log:debug(
<<"Batch send completed"/utf8>>,
[{<<"total"/utf8>>, gleam@string:inspect(Total)},
{<<"successful"/utf8>>, gleam@string:inspect(Successes)},
{<<"failed"/utf8>>, gleam@string:inspect(erlang:length(Errors))}]
),
{batch_send_result, Total, Successes, erlang:length(Errors), Errors}.
-file("src/distribute/messaging.gleam", 170).
?DOC(" Send batch and return Ok only if all succeeded, Error with first failure otherwise\n").
-spec send_batch_strict(list({binary(), any()})) -> {ok, nil} |
{error, send_error()}.
send_batch_strict(Messages) ->
Result = send_batch(Messages),
case erlang:element(4, Result) of
0 ->
{ok, nil};
_ ->
case gleam@list:first(erlang:element(5, Result)) of
{ok, E} ->
{error, E};
{error, _} ->
{error, {send_failed, <<"Unknown batch error"/utf8>>}}
end
end.