<?xml version="1.0" encoding="latin1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> <year>1997</year><year>2009</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> The contents of this file are subject to the Erlang Public License, Version 1.1, (the "License"); you may not use this file except in compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. </legalnotice> <title>Overview</title> <prepared></prepared> <docno></docno> <date></date> <rev></rev> <file>des_princ.xml</file> </header> <p>The <em>OTP Design Principles</em> is a set of principles for how to structure Erlang code in terms of processes, modules and directories.</p> <section> <title>Supervision Trees</title> <p>A basic concept in Erlang/OTP is the <em>supervision tree</em>. This is a process structuring model based on the idea of <em>workers</em> and <em>supervisors</em>.</p> <list type="bulleted"> <item>Workers are processes which perform computations, that is, they do the actual work.</item> <item>Supervisors are processes which monitor the behaviour of workers. A supervisor can restart a worker if something goes wrong.</item> <item>The supervision tree is a hierarchical arrangement of code into supervisors and workers, making it possible to design and program fault-tolerant software.</item> </list> <marker id="sup6"></marker> <image file="../design_principles/sup6.gif"> <icaption>Supervision Tree</icaption> </image> <p>In the figure above, square boxes represents supervisors and circles represent workers.</p> </section> <section> <title>Behaviours</title> <p>In a supervision tree, many of the processes have similar structures, they follow similar patterns. For example, the supervisors are very similar in structure. The only difference between them is which child processes they supervise. Also, many of the workers are servers in a server-client relation, finite state machines, or event handlers such as error loggers.</p> <p><em>Behaviours</em> are formalizations of these common patterns. The idea is to divide the code for a process in a generic part (a behaviour module) and a specific part (a <em>callback module</em>).</p> <p>The behaviour module is part of Erlang/OTP. To implement a process such as a supervisor, the user only has to implement the callback module which should export a pre-defined set of functions, the <em>callback functions</em>.</p> <p>An example to illustrate how code can be divided into a generic and a specific part: Consider the following code (written in plain Erlang) for a simple server, which keeps track of a number of "channels". Other processes can allocate and free the channels by calling the functions <c>alloc/0</c> and <c>free/1</c>, respectively.</p> <marker id="ch1"></marker> <code type="none"> -module(ch1). -export([start/0]). -export([alloc/0, free/1]). -export([init/0]). start() -> spawn(ch1, init, []). alloc() -> ch1 ! {self(), alloc}, receive {ch1, Res} -> Res end. free(Ch) -> ch1 ! {free, Ch}, ok. init() -> register(ch1, self()), Chs = channels(), loop(Chs). loop(Chs) -> receive {From, alloc} -> {Ch, Chs2} = alloc(Chs), From ! {ch1, Ch}, loop(Chs2); {free, Ch} -> Chs2 = free(Ch, Chs), loop(Chs2) end.</code> <p>The code for the server can be rewritten into a generic part <c>server.erl</c>:</p> <code type="none"> -module(server). -export([start/1]). -export([call/2, cast/2]). -export([init/1]). start(Mod) -> spawn(server, init, [Mod]). call(Name, Req) -> Name ! {call, self(), Req}, receive {Name, Res} -> Res end. cast(Name, Req) -> Name ! {cast, Req}, ok. init(Mod) -> register(Mod, self()), State = Mod:init(), loop(Mod, State). loop(Mod, State) -> receive {call, From, Req} -> {Res, State2} = Mod:handle_call(Req, State), From ! {Mod, Res}, loop(Mod, State2); {cast, Req} -> State2 = Mod:handle_cast(Req, State), loop(Mod, State2) end.</code> <p>and a callback module <c>ch2.erl</c>:</p> <code type="none"> -module(ch2). -export([start/0]). -export([alloc/0, free/1]). -export([init/0, handle_call/2, handle_cast/2]). start() -> server:start(ch2). alloc() -> server:call(ch2, alloc). free(Ch) -> server:cast(ch2, {free, Ch}). init() -> channels(). handle_call(alloc, Chs) -> alloc(Chs). % => {Ch,Chs2} handle_cast({free, Ch}, Chs) -> free(Ch, Chs). % => Chs2</code> <p>Note the following:</p> <list type="bulleted"> <item>The code in <c>server</c> can be re-used to build many different servers.</item> <item>The name of the server, in this example the atom <c>ch2</c>, is hidden from the users of the client functions. This means the name can be changed without affecting them.</item> <item>The protcol (messages sent to and received from the server) is hidden as well. This is good programming practice and allows us to change the protocol without making changes to code using the interface functions.</item> <item>We can extend the functionality of <c>server</c>, without having to change <c>ch2</c> or any other callback module.</item> </list> <p>(In <c>ch1.erl</c> and <c>ch2.erl</c> above, the implementation of <c>channels/0</c>, <c>alloc/1</c> and <c>free/2</c> has been intentionally left out, as it is not relevant to the example. For completeness, one way to write these functions are given below. Note that this is an example only, a realistic implementation must be able to handle situations like running out of channels to allocate etc.)</p> <code type="none"> channels() -> {_Allocated = [], _Free = lists:seq(1,100)}. alloc({Allocated, [H|T] = _Free}) -> {H, {[H|Allocated], T}}. free(Ch, {Alloc, Free} = Channels) -> case lists:member(Ch, Alloc) of true -> {lists:delete(Ch, Alloc), [Ch|Free]}; false -> Channels end. </code> <p>Code written without making use of behaviours may be more efficient, but the increased efficiency will be at the expense of generality. The ability to manage all applications in the system in a consistent manner is very important.</p> <p>Using behaviours also makes it easier to read and understand code written by other programmers. Ad hoc programming structures, while possibly more efficient, are always more difficult to understand.</p> <p>The module <c>server</c> corresponds, greatly simplified, to the Erlang/OTP behaviour <c>gen_server</c>.</p> <p>The standard Erlang/OTP behaviours are:</p> <taglist> <tag><seealso marker="gen_server_concepts">gen_server</seealso></tag> <item>For implementing the server of a client-server relation.</item> <tag><seealso marker="fsm">gen_fsm</seealso></tag> <item>For implementing finite state machines.</item> <tag><seealso marker="events">gen_event</seealso></tag> <item>For implementing event handling functionality.</item> <tag><seealso marker="sup_princ">supervisor</seealso></tag> <item>For implementing a supervisor in a supervision tree.</item> </taglist> <p>The compiler understands the module attribute <c>-behaviour(Behaviour)</c> and issues warnings about missing callback functions. Example:</p> <code type="none"> -module(chs3). -behaviour(gen_server). ... 3> c(chs3). ./chs3.erl:10: Warning: undefined call-back function handle_call/3 {ok,chs3}</code> </section> <section> <title>Applications</title> <p>Erlang/OTP comes with a number of components, each implementing some specific functionality. Components are with Erlang/OTP terminology called <em>applications</em>. Examples of Erlang/OTP applications are Mnesia, which has everything needed for programming database services, and Debugger which is used to debug Erlang programs. The minimal system based on Erlang/OTP consists of the applications Kernel and STDLIB.</p> <p>The application concept applies both to program structure (processes) and directory structure (modules).</p> <p>The simplest kind of application does not have any processes, but consists of a collection of functional modules. Such an application is called a <em>library application</em>. An example of a library application is STDLIB.</p> <p>An application with processes is easiest implemented as a supervision tree using the standard behaviours.</p> <p>How to program applications is described in <seealso marker="applications">Applications</seealso>.</p> </section> <section> <title>Releases</title> <p>A <em>release</em> is a complete system made out from a subset of the Erlang/OTP applications and a set of user-specific applications.</p> <p>How to program releases is described in <seealso marker="release_structure">Releases</seealso>.</p> <p>How to install a release in a target environment is described in the chapter about Target Systems in System Principles.</p> </section> <section> <title>Release Handling</title> <p><em>Release handling</em> is upgrading and downgrading between different versions of a release, in a (possibly) running system. How to do this is described in <seealso marker="release_handling">Release Handling</seealso>.</p> </section> </chapter>