macula_response behaviour (macula v15.4.0)
View SourceBehaviour for supervised RPC responses.
advertise/5 on the raw SDK takes a bare handler fun invoked in a transient process spawned per inbound CALL (see the internal macula_station_link advertise/5 — "Handlers run in a transient process spawned per CALL"). This module gives that transient process a proper shape: each inbound call starts one supervised macula_response child (under a simple_one_for_one factory this module owns), threading state through Module:init/1 and Module:handle_request/2, and publishing rpc.received_v1 / rpc.replied_v1 mesh facts around the request. This is the provider-side counterpart to macula_request.
A crashing Module:handle_request/2 kills the response child; that composes with the SDK's own crash mapping unchanged, since gen_server:call/3 against a dead callee raises the same way a crashing bare handler fun already does.
Example
-module(math_service).
-behaviour(macula_response).
-export([init/1, handle_request/2]).
init(_Args) -> {ok, []}.
handle_request(#{a := A, b := B}, State) ->
{reply, #{result => A + B}, State}. {ok, _Sup} = macula_response:advertise(Pool, Realm,
<<"math.add_v1">>, math_service, []).Advertise and publish functions
The options of advertise/6 and advertise_direct/7 take advertise, the function the handler is advertised with, macula:advertise/5 by default; and fact_publish, the function each response announces its facts with, macula:publish/4 by default. The other options go on to those functions without these two. A test gives its own functions this way instead of replacing a module.
Direct-dial
advertise/5,6 registers the handler with the pool, and since 14.1.0 that alone makes it resolvable: each link sends its station an ADVERTISE and puts the same signed procedure_advertisement in the DHT, signed once by the pool and naming that link's station (macula#33). A caller using macula_request:start_link_direct/6,7 resolves it and dials in one hop, whether or not the two stations have a routing edge between them. advertise_direct/6,7 is the same registration, kept for the 14.x API.
Summary
Functions
Advertise Procedure on Pool/Realm. Starts a private factory supervisor for per-request response children and registers a dispatch handler with macula:advertise/5. Returns the supervisor pid so the caller can supervise it (or ignore it).
As advertise/5. Opts may include announce (default true), auth (forwarded to macula:advertise/5), handler_timeout_ms — how long to wait for the handler before the caller is answered temporary_relay_failure, an integer from 1 to 600000, default 30000; anything else is refused as {error, {invalid_handler_timeout_ms, Value}} — and reuse_sup — an existing supervisor pid (as returned by a prior advertise/5,6 call) to register the handler again with, without starting a new factory supervisor. Use this for a periodic re-advertise (see advertise_direct/6,7's own doc) — calling plain advertise/5,6 on a timer would leak one orphaned supervisor per tick, since each call otherwise starts a fresh one.
As advertise/5. Since 14.1.0 advertise/5 itself makes the provider resolvable: each link puts the advertisement it sends its station in the DHT, signed once by the pool (macula#33), so this publishes nothing of its own. NodeIdentity is kept for the 14.x signature and is not used.
As advertise_direct/6, with Opts forwarded to advertise/6. cert_chain, a 10.x option authorization replaces, is refused with {error, {removed_option, cert_chain}} before the handler is registered.
The verified caller of the request this process is answering, or undefined when it answers none. Set by the link for every payload shape (macula#60), so handle_request/2 can attribute a non-map payload like a map one; a map payload also keeps the merged caller key (with_caller/2).
Stop advertising. Does not stop the factory supervisor returned by advertise/5,6 — callers that want to tear it down should exit(Sup, shutdown) themselves.
Types
-type advertise() :: fun((macula:pool(), macula:realm(), macula:procedure(), macula_client:handler(), map()) -> ok | {error, term()}).
-type advertise_opts() :: #{advertise => advertise(), fact_publish => macula_lifetime_announcer:publish(), handler_timeout_ms => 1..600000, atom() => term()}.
Callbacks
Functions
-spec advertise(macula:pool(), macula:realm(), macula:procedure(), module(), term()) -> {ok, pid()} | {error, term()}.
Advertise Procedure on Pool/Realm. Starts a private factory supervisor for per-request response children and registers a dispatch handler with macula:advertise/5. Returns the supervisor pid so the caller can supervise it (or ignore it).
-spec advertise(macula:pool(), macula:realm(), macula:procedure(), module(), term(), advertise_opts()) -> {ok, pid()} | {error, term()}.
As advertise/5. Opts may include announce (default true), auth (forwarded to macula:advertise/5), handler_timeout_ms — how long to wait for the handler before the caller is answered temporary_relay_failure, an integer from 1 to 600000, default 30000; anything else is refused as {error, {invalid_handler_timeout_ms, Value}} — and reuse_sup — an existing supervisor pid (as returned by a prior advertise/5,6 call) to register the handler again with, without starting a new factory supervisor. Use this for a periodic re-advertise (see advertise_direct/6,7's own doc) — calling plain advertise/5,6 on a timer would leak one orphaned supervisor per tick, since each call otherwise starts a fresh one.
-spec advertise_direct(macula:pool(), macula:realm(), macula:procedure(), module(), term(), macula_node_keys:node_key()) -> {ok, pid()} | {error, term()}.
As advertise/5. Since 14.1.0 advertise/5 itself makes the provider resolvable: each link puts the advertisement it sends its station in the DHT, signed once by the pool (macula#33), so this publishes nothing of its own. NodeIdentity is kept for the 14.x signature and is not used.
-spec advertise_direct(macula:pool(), macula:realm(), macula:procedure(), module(), term(), macula_node_keys:node_key(), advertise_opts()) -> {ok, pid()} | {error, term()}.
As advertise_direct/6, with Opts forwarded to advertise/6. cert_chain, a 10.x option authorization replaces, is refused with {error, {removed_option, cert_chain}} before the handler is registered.
The verified caller of the request this process is answering, or undefined when it answers none. Set by the link for every payload shape (macula#60), so handle_request/2 can attribute a non-map payload like a map one; a map payload also keeps the merged caller key (with_caller/2).
-spec unadvertise(macula:pool(), macula:realm(), macula:procedure()) -> ok.
Stop advertising. Does not stop the factory supervisor returned by advertise/5,6 — callers that want to tear it down should exit(Sup, shutdown) themselves.