[erlang-questions] Best practices for handling invalid / unexpected messages

Ahmed Omar spawn.think@REDACTED
Fri May 13 22:33:00 CEST 2011


On Fri, May 13, 2011 at 10:19 PM, Ciprian Dorin Craciun <
ciprian.craciun@REDACTED> wrote:

> 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?
>
>
No. In case data is from inside your system, you might assume it's correct.
(specially if validation will cause too much overhead).

>
> > 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.
>
>
> Actually monitors are not that hard at all, you just need to understand
them correctly. You still can use links and trap exit on your main process,
but i personally don't prefer this way (only a preference)
 I would recommend checking LYSE
http://learnyousomeerlang.com/errors-and-processes

> > 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
>



-- 
Best Regards,
- Ahmed Omar
http://nl.linkedin.com/in/adiaa
Follow me on twitter
@spawn_think <http://twitter.com/#!/spawn_think>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20110513/33d38bcc/attachment.htm>


More information about the erlang-questions mailing list