Advanced Programming – Erlang for Robust Systems
Advanced Programming
Erlang for Robust Systems
Ken Friis Larsen
kflarsen@diku.dk
Department of Computer Science
University of Copenhagen
October 9, 2018
1 / 16
Today’s Menu
I Recap Linking processes
I Supervisors
I Library code for making robust servers
I Open Telecom Platform (OTP)
2 / 16
Robust Systems
I We need at least two
computers(/nodes/processes) to make a
robust system: one
computer(/node/process) to do what we
want, and one to monitor the other and
take over when errors happens.
I link(Pid) makes a symmetric link
between the calling process and Pid.
I monitor(process, Pid) makes an
asymmetric link between the calling
process and Pid.
S W
P1 P2
3 / 16
Linking Processes
I If we want to handle when a linked process crashes then we
need to call process_flag(trap_exit, true).
I Thus, we have the following idioms for creating processes:
I Idiom 1, I don’t care:
Pid = spawn(fun() -> … end)
I Idiom 2, I won’t live without her:
Pid = spawn_link(fun() -> … end)
I Idiom 3, I’ll handle the mess-ups:
…
process_flag(trap_exit, true),
Pid = spawn_link(fun() -> … end),
loop(…).
loop(State) ->
receive
{‘EXIT’, Pid, Reason} -> HandleMess, loop(State);
…
end.
4 / 16
Example: Keep Trucking Looping
Suppose that we really most have a phonebook server running at all
times. How do we monitor the phonebook server and restart it if
(when?) it crashes.
5 / 16
Example: Keep Looping
start() -> keep_looping().
request_reply(Pid, Request) -> Ref = make_ref(),
Pid ! {self(), Ref, Request},
receive {Ref, Response} -> Response end.
keep_looping() ->
spawn(fun () ->
process_flag(trap_exit, true),
Worker = spawn_link(fun() -> loop(#{}) end),
supervisor(Worker)
end).
supervisor(Worker) ->
receive
{‘EXIT’, Pid, Reason} ->
io:format(“~p exited because of ~p~n”, [Pid,Reason]),
Pid1 = spawn_link(fun() -> loop(#{}) end),
supervisor(Pid1);
Msg -> Pid ! Msg, supervisor(Pid)
end.
6 / 16
Generic Servers
I Goal: Abstract out the difficult handling of concurrency to a
generic library
I The difficult parts:
I The start–request_reply(/async)–loop pattern
I Supervisors
I Hot-swapping of code
7 / 16
Vanilla Server Library
start(Mod) ->
spawn(fun() -> loop(Mod, Mod:init()) end).
request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive
{Pid, Reply} -> Reply
end.
loop(Mod, State) ->
receive
{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {self(), Reply},
loop(Mod, State1)
end.
8 / 16
Basic Server Library
start(Name, Mod) ->
register(Name, spawn(fun() -> loop(Name, Mod, Mod:init())
end)).
request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive
{Pid, Reply} -> Reply
end.
loop(Name, Mod, State) ->
receive
{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)
end.
9 / 16
Example: Phonebook Callback Module, 1
-module(pb).
-import(basicserver, [request_reply/2]).
%% Interface
start() -> basicserver:start(phonebook, pb).
add(Contact) -> request_reply(phonebook, {add, Contact}).
list_all() -> request_reply(phonebook, list_all).
update(Contact) -> request_reply(phonebook, {update, Contact}).
10 / 16
Example: Phonebook Callback Module, 2
%% Callback functions
init() -> #{}.
handle({add, {Name, _, _} = Contact}, Contacts) ->
case maps:is_key(Name, Contacts) of
false -> {ok, Contacts#{Name => Contact}};
true -> {{error, Name, is_already_there},
Contacts}
end;
handle(list_all, Contacts) ->
List = maps:to_list(Contacts),
{{ok, lists:map(fun({_, C}) -> C end, List)},
Contacts};
handle({update, {Name, _, _} = Contact}, Contacts) ->
{ok, Contacts#{Name => Contact}}.
11 / 16
Hot Code Swapping
swap_code(Name, Mod) -> request_reply(Name, {swap_code, Mod}).
request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive {Pid, Reply} -> Reply
end.
loop(Name, Mod, State) ->
receive
{From, {swap_code, NewMod}} ->
From ! {Name, ok},
loop(Name, NewMod, State);
{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)
end.
12 / 16
Open Telecom Platform (OTP)
I Library(/framework/platform) for building large-scale,
fault-tolerant, distributed applications.
I A central concept is the OTP behaviour
I Some behaviours
I supervisor
I gen_server
I gen_statem (or gen_fsm)
I gen_event
I See proc_lib and sys modules for basic building blocks.
13 / 16
Using gen_server
I Step 1: Decide module name
I Step 2: Write client interface functions
I Step 3: Write the six server callback functions:
I init/1
I handle_call/3
I handle_cast/2
I handle_info/2
I terminate/2
I code_change/3
(you can implement the callback functions by need.)
14 / 16
Summary
I To make a robust system we need two parts: one to do the job
and one to take over in case of errors
I Structure your code into the infrastructure parts and the
functional parts.
I Use gen_server for building robust servers.
I This week’s assignment: Flamingo
15 / 16
Exam
I One week take-home project (2/11–9/11)
I Hand in via Digital Exam
I Max group size is 1 (one)
I The University has a zero-tolerance policy against exam fraud
(including assisting).
16 / 16