[erlang-questions] Processes & Fault Tolerance

Edmond Begumisa ebegumisa@REDACTED
Mon Jan 3 17:30:03 CET 2011


> Let's start with how we do error recovery.
>
> Imagine two linked processes A and B on separate machines. A is the
> master process.
> B is a passive processes that will take over if A fails.
>
> A sends a stream of state update messages S1, S2, S3,... to B. The
> state messages contain enough information
> for B to do whatever A was doing should A fail. If A fails B will
> receive an EXIT signal.
>
> If B does receive an exit signal it knows A has failed, so it carries
> on doing whatever A was doing
> using the information it last received in a state update message.
>
> That's it and all about it - nothing to do with supervisors etc,

Ooooohh! THANK YOU, THANK YOU, THANK YOU.

*Now* I get it. This is precisely what I was trying to understand. The  
missing link was sending redundant messages. It all makes sense now --  
it's so simple. All I have to do to get fault tolerance at the process  
level is have a group of N redundant processes waiting for exit-signals  
and forward state changes to N-1 of them. I could even send to N/2 and  
have some decent tolerance at the expense of consistency of the nodes.

To preemptively answer the question I sent Ulf and Mazen... for that  
distributed database, Process B would be set up to also receive read  
requests forwarded from Process A but not respond to them unless it gets  
an exit signal it can understand like disk_fail_error. Any changes in  
state would also be forwarded to existing redundant processes.

Thanks everyone for your patience in helping me understand  
fault-tolerance. I've already started thinking about some cool things I  
can do with this in my program.

- Edmond -



On Tue, 04 Jan 2011 00:36:30 +1100, Joe Armstrong <erlang@REDACTED> wrote:

> I think you are getting confused between OTP supervisors etc. and the
> general notion of "take-over"
>
> Let's start with how we do error recovery.
>
> Imagine two linked processes A and B on separate machines. A is the
> master process.
> B is a passive processes that will take over if A fails.
>
> A sends a stream of state update messages S1, S2, S3,... to B. The
> state messages contain enough information
> for B to do whatever A was doing should A fail. If A fails B will
> receive an EXIT signal.
>
> If B does receive an exit signal it knows A has failed, so it carries
> on doing whatever A was doing
> using the information it last received in a state update message.
>
> That's it and all about it - nothing to do with supervisors etc,
> <aside> This is how we do things in a distributed system, obviously
> real fault tolerance needs more than one machine to cover the case
> where an entire machine fails. This is also why we copy everything, if
> an entire machine has failed and we have a pointer to vital data on
> that machine then we are in big trouble.
>
> If it works like this in the distributed case then we reasoned it
> should work exactly the same way when both processes are on the same
> machine. Why is this? - because it would be very difficult to program
> if the primitives and mechanisms involved were completely different
> when the remote process lay on the same machine as the process it was
> talking to or on a different machine.
>
> It might be that we actually handle these two cases differently in the
> VM, but as far as the programmer is concerned the system should behave
> the same way, and the only observable change should
> concerned with latencies.
> </aside>
>
> To make the above possible we need a couple of primitives in Erlang,
> namely process_flag trap_exits, and spawn_link. That's all you need.
>
> The basic system properties you need to make a fault-tolerant systems
> are the abilities to detect remote errors and send and receive messages.
>
> What the OTP supervisors etc. do are build layers of abstraction on
> top of spawn-link and trap_exits
> in order to simplify programming common use-cases.
>
> Fault detection and recovery is not arbitrary or magic in any sense,
> It has to be part of the system architecture like everything else. One
> common fault in making systems is to specify and design for the normal
> behavior
> but not specify and design how fault-detection and correction work.
> This is probably due to a legacy of
> programming in languages that have very limited ability to detect
> errors and recover from them.
>
> A C programmer with a single thread of control will take extreme
> measures to avoid crashing their program
> they have to,  there is only one thread of control. Given multiple
> threads of control and the ability to remotely
> detect errors your entire way of thinking changes and the emphasis
> changes from thinking "how to I avoid a crash"
> to thinking "given that a crash has occurred and been detected, how to
> I fix things up and carry on"
>
> The Erlang philosophy is that "things will crash", so we have to
> detect the crashes, fix the bit that broke and
> carry on - we do not make excessive efforts to avoid crashes, but we
> do try very hard to repair things after they have gone wrong.
>
> Fixing things after they have broken is often easier than preventing
> them from breaking. Preventing them
> from breaking is not theoretically possible - we can't even prove
> simple things, like that a program will terminate
> (the famous halting problem) - but we can easily detect failures and
> put the system to rights by applying
> certain invariants.
>
> OTP supervisors are just trees of processes with certain restart rules
> that experience has shown us
> to be useful.
>
> The main value of supervisors etc. (and of all the OTP behaviors) is
> in building large systems with many
> programmers involved. Rather than inventing their own patterns, the
> programmers reuse a common set of
> patterns, which makes the projects easier to manage.
>
> /Joe
>
>
>
>
> On Mon, Jan 3, 2011 at 6:21 AM, Edmond Begumisa
> <ebegumisa@REDACTED> wrote:
>> Thanks for your response.
>>
>> Firstly, let me make my question a little clearer...
>>
>> To rephrase: For processes, "share nothing for the sake of concurrency"  
>> - I
>> get, both in concept and application. "Share nothing for the sake of
>> fault-tolerance" - I get in concept but not in application.
>>
>> Yet as I understand it, it is for the latter reason Erlang shares  
>> nothing*
>> and not the former. Interpretation: I must be completely missing the  
>> point
>> in regards to Erlang processes and sharing nothing. This is what I want  
>> to
>> understand in application. In addition to the "side" effect of sane
>> concurrency (which coming from a chaotic multi-threading shared-memory  
>> world
>> I fully appreciate and practically make use of everyday), how can I also
>> make use of the "real" reason Erlang processes share nothing -- fault
>> tolerance? Practically/illustratively speaking?
>>
>> *ETS being the obvious exception.
>>
>> Secondly, here's a mantra from Joe Armstrong...
>>
>> @ minute 17:26
>> http://www.se-radio.net/2008/03/episode-89-joe-armstrong-on-erlang/
>>
>> "[message passing concurrency]... the original reasons have to do with  
>> fault
>> tolerance... you have to copy all the data you need from computer 1 to
>> computer 2... if computer 1 crashes you take over on computer 2... you  
>> can't
>> have dangling pointers... that's the reason for copying everything...  
>> it's
>> got nothing to do with concurrency, it's got a lot to do with
>> fault-tolerance... if they don't crash you could just have a dangling
>> pointer and copy less data but it won't work in the presence of  
>> errors..."
>>
>> I interpret this to mean that share-nothing between processes is more  
>> about
>> replicating valid state than isolating corrupted state as you described.
>>
>> Indeed, Joe created an example on his blog...
>>
>> http://armstrongonsoftware.blogspot.com/2007/07/scalable-fault-tolerant-upgradable.html
>>
>> It's algorithm 3 there I'm struggling with. Particularly where he  
>> says...
>>
>> "... In practise we would send an asynchronous stream of messages from  
>> N to
>> N+1 containing enough information to recover if things go wrong."
>>
>> Unfortunately, I couldn't find part II to that post (I don't think  
>> there is
>> one.) And I'm too green and inexperienced in the field of fault-tolerant
>> systems to figure it out on my own. I'm having trouble visualising the
>> practical here from the conceptual -- I need to be shown how :(
>>
>> Also, I seem to be under the impression that the Erlang language has  
>> some
>> sort of schematics to do this built-in (i.e. deal with one process  
>> taking
>> over from another if the first fails) and this is the reason processes  
>> share
>> nothing. This seems to me to be something different from supervision  
>> trees,
>> which use exit-trapping to re-spawn if a process fails with the active  
>> 'job'
>> disappearing and any errors logged (like restarting a daemon). My
>> interpretation of the fault-tolerance Erlang is supposed to enable (for
>> those in the know) is seamless take-over. The 'job' lives on but  
>> elsewhere.
>>
>> Using telecoms as an example: a phone call wouldn't be cut-off when a  
>> fault
>> occurs, another node would seamlessly take over. This is how I  
>> interpreted
>> Joe's post and other descriptions of Erlang's fault-tolerant features  
>> and I
>> understand the key is in the share nothing policy for processes. I'm  
>> sure
>> I've mis-understood something or everything :)
>>
>> - Edmond -
>>
>> PS: I've read the Manning draft. Great book. I don't know if the answer  
>> lies
>> in OTP (I searched and didn't find it). I suspect it's lower --  
>> probalby how
>> you organise your processes. Some distributed-programming black-magic  
>> only
>> Erlanger's know about :)
>>
>>
>> On Mon, 03 Jan 2011 14:56:51 +1100, Alain O'Dea <alain.odea@REDACTED>
>> wrote:
>>
>>> On 2011-01-02, at 22:36, "Edmond Begumisa"  
>>> <ebegumisa@REDACTED>
>>> wrote:
>>>
>>>> Slight correction...
>>>>
>>>> On Mon, 03 Jan 2011 12:38:38 +1100, Edmond Begumisa
>>>> <ebegumisa@REDACTED> wrote:
>>>>
>>>>> Hello all,
>>>>>
>>>>> I've been trying to wrap my Erlang's fault tolerant features
>>>>> particularly in relation to processes.
>>>>>
>>>>
>>>> Should be: I've been trying to wrap my head around Erlang's fault
>>>> tolerant features particularly in relation to processes.
>>>>
>>>> Sorry.
>>>>
>>>>> I've heard/read repeatedly that the primary reason why Erlang's
>>>>> designers opted for a share-nothing policy is not rooted in  
>>>>> concurrency but
>>>>> rather in fault-tolerance. When nothing is shared, everything is  
>>>>> copied.
>>>>> When everything is copied processes can take over from one another  
>>>>> when
>>>>> things fail. I follow this reasoning but I don't follow how to apply  
>>>>> it.
>>>>>
>>>>> I fully understand and appreciate how supervision trees are used to
>>>>> restart processes if they fail. What I don't get is what to do when  
>>>>> you
>>>>> don't want to restart but want to take over, say on another node. I  
>>>>> know
>>>>> that at a higher-level, OTP has some take-over/fail-over schematics  
>>>>> (at the
>>>>> application level.) I'm trying to understand things at the processes  
>>>>> level -
>>>>> why Erlang is the way it is so I can better use it to make my  
>>>>> currently
>>>>> fault-intolerant program fault tolerant.
>>>>>
>>>>> Specifically, how can one process take over from another if it  
>>>>> fails? It
>>>>> appears to may that the only way to do this would be to somehow  
>>>>> retrieve not
>>>>> only the state of the process (say, gen_server's state) but also the
>>>>> messages in its mailbox. Where does the design decision to  
>>>>> share-nothing for
>>>>> the sake of fault-tolerance come into play for processes? Please  
>>>>> help me
>>>>> "get" this!
>>>>>
>>>>> Thanks in advance.
>>>>>
>>>>> - Edmond -
>>>>>
>>>>>
>>>
>>> Hi Edmond:
>>>
>>> Share-nothing helps with concurrent fault-tolerance by preventing one
>>> process from corrupting the state of another. Receive is a process'  
>>> choice
>>> and it corrupts its own state if it receives bad data and lets it in.
>>>
>>> AFAIK OTP fault-tolerance doesn't mean no requests will fail, it means  
>>> the
>>> system/sub-system will recover if a single request causes a process to
>>> crash.  It's kind of like proper try/catch recovery applied to  
>>> concurrent
>>> code.  How you recover from the crash depends on the supervision  
>>> strategy
>>> chosen.  In some cases the supervisor can pass the state to the  
>>> replacement
>>> process. In others this isn't necessary or even desirable since the  
>>> state
>>> itself may involve resources lost in the crash or corrupted state that  
>>> led
>>> to the crash.
>>>
>>> I am straying outside my knowledge here so this paragraph is guesswork.
>>>  The message queue for a gen_server need not necessarily be lost when  
>>> the
>>> callback module crashes.  In theory OTP could (and might already)  
>>> simply
>>> delegate the messages to the replacement process following a crash.  
>>>  Someone
>>> who knows OTP better than me would need to weigh in here though.
>>>
>>> I found http://manning.com/logan very informative in understanding OTP  
>>> and
>>> its supervisor hierarchies.
>>>
>>> Cheers,
>>> Alain
>>> ________________________________________________________________
>>> erlang-questions (at) erlang.org mailing list.
>>> See http://www.erlang.org/faq.html
>>> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>>>
>>
>>
>> --
>> Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
>>
>> ________________________________________________________________
>> erlang-questions (at) erlang.org mailing list.
>> See http://www.erlang.org/faq.html
>> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>>
>>
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>


-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/


More information about the erlang-questions mailing list