Erlang -- Overview

OTP Design Principles

User's Guide

Version 12.0

- User's Guide
- PDF
- Top

- Expand All
- Contract All

### Chapters

- Overview
  - Top of chapter
  - Supervision Trees
  - Behaviours
  - Applications
  - Releases
  - Release Handling
- gen_server Behaviour
  - Top of chapter
  - Client-Server Principles
  - Example
  - Starting a Gen_Server
  - Synchronous Requests - Call
  - Asynchronous Requests - Cast
  - Stopping
  - Handling Other Messages
- gen_statem Behaviour
  - Top of chapter
  - Event-Driven State Machines
  - When to use gen_statem
  - Callback Module
  - Callback Modes
  - State Callback
  - Transition Actions
  - Event Types and Event Content
  - State Enter Calls
  - Time-Outs
  - Example
  - Starting gen_statem
  - Handling Events
  - State Time-Outs
  - All State Events
  - One State Callback
  - Stopping
  - Event Time-Outs
  - Generic Time-Outs
  - Erlang Timers
  - Postponing Events
  - State Enter Actions
  - Inserted Events
  - Example Revisited
  - Filter the State
  - Complex State
  - Hibernation
- gen_event Behaviour
  - Top of chapter
  - Event Handling Principles
  - Example
  - Starting an Event Manager
  - Adding an Event Handler
  - Notifying about Events
  - Deleting an Event Handler
  - Stopping
  - Handling Other Messages
- Supervisor Behaviour
  - Top of chapter
  - Supervision Principles
  - Example
  - Supervisor Flags
  - Restart Strategy
  - Maximum Restart Intensity
  - Automatic Shutdown
  - Child Specification
  - Starting a Supervisor
  - Adding a Child Process
  - Stopping a Child Process
  - Simplified one_for_one Supervisors
  - Stopping
- sys and proc_lib
  - Top of chapter
  - Simple Debugging
  - Special Processes
  - User-Defined Behaviours
- Applications
  - Top of chapter
  - Application Concept
  - Application Callback Module
  - Application Resource File
  - Directory Structure
  - Application Controller
  - Loading and Unloading Applications
  - Starting and Stopping Applications
  - Configuring an Application
  - Application Start Types
- Included Applications
  - Top of chapter
  - Introduction
  - Specifying Included Applications
  - Synchronizing Processes during Startup
- Distributed Applications
  - Top of chapter
  - Introduction
  - Specifying Distributed Applications
  - Starting and Stopping Distributed Applications
  - Failover
  - Takeover
- Releases
  - Top of chapter
  - Release Concept
  - Release Resource File
  - Generating Boot Scripts
  - Creating a Release Package
  - Directory Structure
- Release Handling
  - Top of chapter
  - Release Handling Principles
  - Requirements
  - Distributed Systems
  - Release Handling Instructions
  - Application Upgrade File
  - Release Upgrade File
  - Installing a Release
  - Updating Application Specifications
- Appup Cookbook
  - Top of chapter
  - Changing a Functional Module
  - Changing a Residence Module
  - Changing a Callback Module
  - Changing Internal State
  - Module Dependencies
  - Changing Code for a Special Process
  - Changing a Supervisor
  - Adding or Deleting a Module
  - Starting or Terminating a Process
  - Adding or Removing an Application
  - Restarting an Application
  - Changing an Application Specification
  - Changing Application Configuration
  - Changing Included Applications
  - Changing Non-Erlang Code
  - Emulator Restart and Upgrade
  - Emulator Upgrade From Pre OTP R15

# 1 Overview

The OTP design principles define how to structure Erlang code in terms of processes, modules, and directories.

### 1.1 Supervision Trees

A basic concept in Erlang/OTP is the supervision tree. This is a process structuring model based on the idea of workers and supervisors:

- Workers are processes that perform computations, that is, they do the actual work.
- Supervisors are processes that monitor the behaviour of workers. A supervisor can restart a worker if something goes wrong.
- The supervision tree is a hierarchical arrangement of code into supervisors and workers, which makes it possible to design and program fault-tolerant software.

In the following figure, square boxes represents supervisors and circles represent workers:

Figure 1.1: Supervision Tree

### 1.2 Behaviours

In a supervision tree, many of the processes have similar structures, they follow similar patterns. For example, the supervisors are similar in structure. The only difference between them is which child processes they supervise. Many of the workers are servers in a server-client relation, finite-state machines, or event handlers.

Behaviours 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 callback module).

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 is to export a pre-defined set of functions, the callback functions.

The following example 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 alloc/0 and free/1, respectively.

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

The code for the server can be rewritten into a generic part server.erl:

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

And a callback module ch2.erl:

    -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

Notice the following:

- The code in server can be reused to build many different servers.
- The server name, in this example the atom ch2, is hidden from the users of the client functions. This means that the name can be changed without affecting them.
- The protocol (messages sent to and received from the server) is also hidden. This is good programming practice and allows one to change the protocol without changing the code using the interface functions.
- The functionality of server can be extended without having to change ch2 or any other callback module.

In ch1.erl and ch2.erl above, the implementation of channels/0, alloc/1, and free/2 has been intentionally left out, as it is not relevant to the example. For completeness, one way to write these functions is given below. This is an example only, a realistic implementation must be able to handle situations like running out of channels to allocate, and so on.

    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 written without using behaviours can be more efficient, but the increased efficiency is at the expense of generality. The ability to manage all applications in the system in a consistent manner is important.

Using behaviours also makes it easier to read and understand code written by other programmers. Improvised programming structures, while possibly more efficient, are always more difficult to understand.

The server module corresponds, greatly simplified, to the Erlang/OTP behaviour gen_server.

The standard Erlang/OTP behaviours are:

- gen_server

  For implementing the server of a client-server relation

- gen_statem

  For implementing state machines

- gen_event

  For implementing event handling functionality

- supervisor

  For implementing a supervisor in a supervision tree

The compiler understands the module attribute -behaviour(Behaviour) and issues warnings about missing callback functions, for example:

    -module(chs3).
    -behaviour(gen_server).
    ...

    3> c(chs3).
    ./chs3.erl:10: Warning: undefined call-back function handle_call/3
    {ok,chs3}

### 1.3 Applications

Erlang/OTP comes with a number of components, each implementing some specific functionality. Components are with Erlang/OTP terminology called applications. 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 following two applications:

- Kernel - Functionality necessary to run Erlang
- STDLIB - Erlang standard libraries

The application concept applies both to program structure (processes) and directory structure (modules).

The simplest applications do not have any processes, but consist of a collection of functional modules. Such an application is called a library application. An example of a library application is STDLIB.

An application with processes is easiest implemented as a supervision tree using the standard behaviours.

How to program applications is described in Applications.

### 1.4 Releases

A release is a complete system made out from a subset of Erlang/OTP applications and a set of user-specific applications.

How to program releases is described in Releases.

How to install a release in a target environment is described in the section about target systems in Section 2 System Principles.

### 1.5 Release Handling

Release handling is upgrading and downgrading between different versions of a release, in a (possibly) running system. How to do this is described in Release Handling.