Max
Max

Reputation: 691

Good design for resending a message?

I need to send a message to a globally registered process, that might be unavailable for a short-time, when it is replaced with a backup process (i.e. failover).

Is the following snippet good Erlang code:

% send message to globally registered process, with possibility to retry once
send_message(To, Message, Retry) ->
    try global:send(To, Message)
    catch
        % registered process To is unavailable
        exit: {badarg, {To, Message}} ->
            io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
            case Retry of 
                true ->
                    % retry sending message, after 1 second
                    sleep(1000),
                    send_message(To, Message, false);
                false ->
                    % re-throw caught exit, including stack trace
                    erlang:raise(exit, {badarg, {To, Message}}, 
                                 erlang:get_stacktrace())
            end            
    end.

The Retry parameter is either true or false, indicating the message should be retried once if there was a problem. If the message can still not be sent, I want the same exception to be raised as would have been the case by calling global:send(To, Message) outside a try-catch block.

I know the above works, but I am concerned whether the false section of my case block is good erlang (e.g. using erlang:raise() and rlang:get_stacktrace()).

Any thoughts or suggestions to make the code "better"?

Upvotes: 1

Views: 993

Answers (2)

Marcelo Cantos
Marcelo Cantos

Reputation: 185902

Make two different calls to global:send, one inside a try ... catch, the other not:

send_message(To, Message, 0, _, _) ->
    global:send(To, Message);
send_message(To, Message, RetriesLeft, RetryDelayMs, MaxRetryDelayMs) ->
    try
        global:send(To, Message)
    catch
        % registered process To is unavailable
        exit: {badarg, {To, Message}} ->
            io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
            % retry after RetryDelayMs milliseconds
            sleep(min(RetryDelayMs, MaxRetryDelayMs)),
            send_message(To, Message, RetriesLeft - 1, 2 * RetryDelayMs, MaxRetryDelayMs)
    end.

EDIT: Added exponential back-off. Be like … nope, just can't do it.

Upvotes: 9

rvirding
rvirding

Reputation: 20916

I would do

erlang:error({badarg,{To,Message}})

instead. No real difference as this also generates a stack trace but I think it is clearer. erlang:raise/3 is better for a more generic usage and if you want do things with the stacktrace.

Upvotes: 1

Related Questions