Isaac Gouy
Posts: 527
Nickname: igouy
Registered: Jul, 2003
|
|
Re: Failure, Preconditions, and Reuse
|
Posted: Mar 13, 2004 2:28 PM
|
|
I'm posting this for Ulf Wiger - seems that the Artima registration process failed for him :-(
1) If system failure is not an option, you have to go with hardware redundancy, so a process might be allowed to crash the processor/OS it is running on. This is important, as it allows you write "kernel processes", that have to be assumed correct for the node to be operational. In Erlang, you can build a system using multiple "Erlang nodes", where distribution aspects can be either transparent or explicit, depending on the role of your program. This is how redundancy is normally implemented, and it can be done in several ways, depending on requirements: a) Hot standby: typically, a process on another computer would monitor the active process, and the two would employ some replication protocol to stay in synch. This implies quite explicit exception handling on the part of the standby process. However, the logic required can be packaged as a reusable framework, so that the process assuming the active role is notified through a simple callback function. b) Cold standby: The Erlang nodes can be configured so that the applications running on one node will be restarted on another in case of failure. The applications can detect that they are starting due to "failover" from another node, or they can start as they normally do.
2) A process crash does not have to lead to a node crash. Erlang's "process linking" concept can be used in a variety of ways. a) The default behaviour is that if a process dies, all processes linked to it will also die. This is called "cascading exit", and allows you to clean up a fairly large amount of work automatically. b) A process that wants to take action when another dies can trap exits. Example: if process A wants to open a file, the file library spawns a process B that opens the file and acts as a middle man; B becomes A's file handle. If A dies, B, having linked itself to A and trapping exits, detects this (it receives an 'EXIT' message from A), closes the file, and then exits. c) Supervisors are special processes built on the linking concept. If a supervised process dies, it is restarted with default values by its supervisor. If necessary, the supervisor can be configured to restart a group of processes, as this may simplify the re-synchronization. If the restart frequency exceeds a configured limit, the supervisor exits, and lets the next-level supervisor handle the situation (escalated restart.) d) Re-acquiring a process handle may not be necessary. A process can register itself using a logical name, and other processes wanting to talk to it, can use the logical name as the destination for message sending. After a crash, the new process registers under the same name, and other processes may never know the difference.
3) Erlang doesn't really use Design by Contract, but relies rather heavily on pattern matching. For example, The function file:open(File, Mode) is defined so that it returns {ok, FileDescr} or {error, Reason}. A typical call to this function would be formulated:
{ok, Fd} = file:open("foo.txt", [read]).
This means that the caller will assert that the returned value is a 2-tuple where the first element is the constant 'ok', and the second is some object that becomes bound to the free variable Fd. If the function would return e.g. {error, enoent}, the caller would crash. This is called "programming for the correct case", and is widely used in Erlang. It works wonderfully for both large and small systems. Pattern matching can also be used on the inputs to a function. For example the function hd(List), extracting the first element from a linked list, could be written:
hd([Head|_]) -> Head.
Meaning that the function will only accept as input a list containing at least one element (_ is a "don't care" pattern, and in this case represents the tail of the list.) Any other input will cause a function_clause exception. This could also be written explicitly as:
hd([Head|_]) -> Head; hd(Other) -> exit({function_clause, Other}).
/Uffe -- Ulf Wiger
|
|