Current section
Files
Jump to
Current section
Files
src/aion@workflow@run.erl
-module(aion@workflow@run).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aion/workflow/run.gleam").
-export([timestamp_to_milliseconds/1, run_with_default/2, run/1, id/0, now/0, random/0, random_int/2]).
-export_type([timestamp/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.
?MODULEDOC(" workflow.run (recorded activity dispatch) + now + random (determinism bindings)\n").
-opaque timestamp() :: {timestamp, integer()}.
-file("src/aion/workflow/run.gleam", 21).
?DOC(" Return the canonical millisecond representation of a deterministic timestamp.\n").
-spec timestamp_to_milliseconds(timestamp()) -> integer().
timestamp_to_milliseconds(Timestamp) ->
erlang:element(2, Timestamp).
-file("src/aion/workflow/run.gleam", 44).
?DOC(
" Dispatch an activity, supplying the workflow-level default task queue used\n"
" when the activity itself selects none.\n"
"\n"
" Resolution precedence (activity override > workflow default > the named\n"
" `\"default\"` queue) is applied once at the engine schedule seam; this SDK\n"
" only carries both unresolved selections across the boundary. A `None`\n"
" workflow default behaves exactly as `run`.\n"
).
-spec run_with_default(
aion@activity:activity(any(), GJM),
gleam@option:option(binary())
) -> {ok, GJM} | {error, aion@error:activity_error()}.
run_with_default(Activity_value, Workflow_default_task_queue) ->
aion@internal@activity_dispatch:run(
Activity_value,
Workflow_default_task_queue
).
-file("src/aion/workflow/run.gleam", 33).
?DOC(
" Dispatch an activity through the single recorded side-effect boundary.\n"
"\n"
" Plain Gleam workflow code is re-run on replay. The only recorded\n"
" side-effectful path exposed by this SDK is `run` (and later concurrency\n"
" combinators over activities); there is intentionally no generic\n"
" `side_effect(fn)` escape hatch. The activity input is encoded with the\n"
" activity's input `Codec`, the engine dispatches and records via AD, and the\n"
" returned payload is decoded with the output `Codec`.\n"
).
-spec run(aion@activity:activity(any(), GJG)) -> {ok, GJG} |
{error, aion@error:activity_error()}.
run(Activity_value) ->
run_with_default(Activity_value, none).
-file("src/aion/workflow/run.gleam", 58).
?DOC(
" Return AD's recorded deterministic timestamp.\n"
"\n"
" Return the current workflow execution's unique identifier.\n"
"\n"
" The engine assigns a v4 UUID at workflow start and records it in the\n"
" `WorkflowStarted` event. This NIF reads the identifier from the\n"
" process's registered workflow handle, so it is stable across replay.\n"
).
-spec id() -> {ok, binary()} | {error, aion@error:engine_error()}.
id() ->
case aion_flow_ffi:workflow_id() of
{ok, Workflow_id} ->
{ok, Workflow_id};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/workflow/run.gleam", 104).
-spec parse_timestamp(binary()) -> {ok, timestamp()} |
{error, aion@error:engine_error()}.
parse_timestamp(Raw) ->
case gleam_stdlib:parse_int(Raw) of
{ok, Milliseconds} ->
{ok, {timestamp, Milliseconds}};
{error, _} ->
{error,
{engine_failure,
<<"Invalid deterministic timestamp: "/utf8, Raw/binary>>}}
end.
-file("src/aion/workflow/run.gleam", 68).
?DOC(
" This is the only time source exposed to workflow code. Workflow authors must\n"
" not call wall-clock APIs such as Gleam/Erlang clocks from workflow logic, as\n"
" ambient time would desynchronise replay.\n"
).
-spec now() -> {ok, timestamp()} | {error, aion@error:engine_error()}.
now() ->
case aion_flow_ffi:now() of
{ok, Raw_timestamp} ->
parse_timestamp(Raw_timestamp);
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/workflow/run.gleam", 114).
-spec parse_float(binary(), binary()) -> {ok, float()} |
{error, aion@error:engine_error()}.
parse_float(Raw, Label) ->
case gleam_stdlib:parse_float(Raw) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error,
{engine_failure,
<<<<<<"Invalid deterministic "/utf8, Label/binary>>/binary,
": "/utf8>>/binary,
Raw/binary>>}}
end.
-file("src/aion/workflow/run.gleam", 79).
?DOC(
" Draw a deterministic floating-point value from AD's seeded RNG.\n"
"\n"
" The engine keys the RNG seed on WorkflowId + RunId so replay observes the\n"
" same sequence. Workflow authors must not call ambient entropy sources.\n"
).
-spec random() -> {ok, float()} | {error, aion@error:engine_error()}.
random() ->
case aion_flow_ffi:random() of
{ok, Raw_random} ->
parse_float(Raw_random, <<"random"/utf8>>);
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/workflow/run.gleam", 124).
-spec parse_int(binary(), binary()) -> {ok, integer()} |
{error, aion@error:engine_error()}.
parse_int(Raw, Label) ->
case gleam_stdlib:parse_int(Raw) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error,
{engine_failure,
<<<<<<"Invalid deterministic "/utf8, Label/binary>>/binary,
": "/utf8>>/binary,
Raw/binary>>}}
end.
-file("src/aion/workflow/run.gleam", 90).
?DOC(
" Draw a deterministic integer in the engine-defined inclusive range.\n"
"\n"
" Values come from AD's seeded RNG through the FFI boundary; no wall-clock or\n"
" ambient entropy binding is exposed by the SDK.\n"
).
-spec random_int(integer(), integer()) -> {ok, integer()} |
{error, aion@error:engine_error()}.
random_int(Min, Max) ->
case Min > Max of
true ->
{error,
{engine_failure,
<<"Invalid deterministic random_int range: min is greater than max"/utf8>>}};
false ->
case aion_flow_ffi:random_int(
erlang:integer_to_binary(Min),
erlang:integer_to_binary(Max)
) of
{ok, Raw_random} ->
parse_int(Raw_random, <<"random_int"/utf8>>);
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end
end.