[erlang-questions] Best practices for handling invalid / unexpected messages
Ciprian Dorin Craciun
ciprian.craciun@REDACTED
Fri May 13 22:19:42 CEST 2011
On Fri, May 13, 2011 at 22:57, Ahmed Omar <spawn.think@REDACTED> wrote:
> Hello Ciprian,
> IMHO
> A) Define the "Caller" :
> i believe you need to make a distinction between the boundaries of your
> system and the internals.
> - On the boundaries, when other systems can feed data into yours, it's
> better to "validate" the inputs.
> - Introduce client APIs for your gen_server, wrapping/abstracting the actual
> gen_server:call
Indeed I wasn't to specific about the purpose of these processes.
I was referring to the internal application processes, those which are
shielded from the "real" callers through a module / process that
exposes a REST interface or a RPC system where I try to validate the
inputs and give back errors as you've described.
> - Do the validation in these APIs, so the crash will be in the client
> process scope.
About the `gen_server:call` wrappers, should I really do any
validation here if the exposed API is an "internal" one (as described
above not directly exposed to the "network")? It could amount to a bit
of overhead, especially if the validation is not a trivial one and
involves the inspection of a deep structure?
> For the gen_server callbacks itself (handle_call, handle_cast, handle_info),
> i would vote for letting it crash if something wrong got there, because
> 1) Makes your code cleaner, more neat and easier to debug
> 2) Doesn't hide bugs (Why a wrong request was made, and why it was not
> validated on the boundaries, also if the caller doesn't check replies,
> ..etc)
Ok.
> B) Other dependent processes :
> It depends on your design, services provided by that gen_server and kind of
> state it keeps. In some cases, the processes needs to be restarted along
> with it (maybe to do initializations all over again), in other cases they
> don't need that. Define your case, and put the supervision tree that suites
> your needs.
Indeed a supervisor tree helps if both the dependent and
dependency process are created by me and part of the same application.
But if one process is provided by another application I can't resort
anymore to `one_for_all` option, but instead I need to link the
process. The only problem is that in this case the link is not quite
bi-directional: i.e. if my dependency process fails then all the
dependent processes should be restarted, but if only a dependent
process fails, then the dependency could live on. Therefore I think
monitors (inside the dependent process) could be a better solution,
but working with monitors is not as easy as linking.
> I hope this helps
Thanks for your reply, it confirmed / extended my initial hunches.
Ciprian.
> On Fri, May 13, 2011 at 8:46 PM, Ciprian Dorin Craciun
> <ciprian.craciun@REDACTED> wrote:
>>
>> Hello all!
>>
>> Lately I've started programming a bit more seriously in Erlang,
>> and after creating a few `gen_server` or `gen_fsm` components, I've
>> started wondering what is the best way to handle unexpected or invalid
>> requests / messages. (For example in `handle_call`, or `handle_info`
>> or state handlers.) (By invalid requests I mean those that have the
>> correct form (i.e. a tuple `{add, A, B}`) but invalid syntax (i.e. `A`
>> is an atom); by unexpected messages I mean those that fail to even
>> loosely match a valid pattern (i.e. `{some_unrecognized_operation, A,
>> B}`.)
>>
>> I think of three possibilities:
>> * reply back with an error:
>> ~~~~
>> handle_call (Request, _Sender, State) -> {reply, {error,
>> {invalid_request, Request}}, State}.
>> ~~~~
>> * exit with an error;
>> ~~~~
>> handle_call (Request, _Sender, State) -> {stop, {error,
>> {invalid_request, Request}}, State}.
>> ~~~~
>> * don't even create a special catch-all case and just let the
>> process fail with `function_clause` error;
>>
>> Now each of these have advantages or disadvantages:
>> * the first one is "polite" to the caller, letting him retry, but
>> could lead to hidden bugs if the caller doesn't check the return term;
>> * the second one I think fits more the Erlang philosophy of
>> "let-it-crash", but could lead to state loss (i.e. an ETS table);
>> * the last one I consider to be just rude as it doesn't give any
>> information on why it failed;
>>
>> Furthermore, let's say a process depends in his operation on
>> another process, should it link the dependency process, should it
>> monitor, or should it just fail on call?
>>
>> So I ask the other Erlang developers how do they handle these
>> situations?
>>
>> Thanks,
>> Ciprian.
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://erlang.org/mailman/listinfo/erlang-questions
>
>
>
> --
> Best Regards,
> - Ahmed Omar
> http://nl.linkedin.com/in/adiaa
> Follow me on twitter
> @spawn_think
More information about the erlang-questions
mailing list