OTP & gen_server

OTP is a collection of behaviours (generic process patterns), libraries, and design principles:

27.1 What is OTP?

OTP is a collection of behaviours (generic process patterns), libraries, and design principles:

Behaviours: gen_server, gen_statem, gen_event, supervisor, application.

The generic part (message loop, timeouts, debugging, upgrades, system messages) is written once; you provide callback modules with the specific logic.

27.2 A complete gen_server

-module(kv_store).
-behaviour(gen_server).

%% API
-export([start_link/0, put/2, get/1, delete/1, stop/0]).
%% Callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-define(SERVER, ?MODULE).

%%% ---------------- API ----------------
start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
put(Key, Value) -> gen_server:cast(?SERVER, {put, Key, Value}).
get(Key) -> gen_server:call(?SERVER, {get, Key}).
delete(Key) -> gen_server:call(?SERVER, {delete, Key}).
stop() -> gen_server:stop(?SERVER).

%%% ---------------- Callbacks ----------------
init([]) ->
    process_flag(trap_exit, true),          % so terminate/2 runs on supervisor shutdown
    {ok, #{}}.

handle_call({get, Key}, _From, State) ->
    {reply, maps:find(Key, State), State};
handle_call({delete, Key}, _From, State) ->
    {reply, ok, maps:remove(Key, State)};
handle_call(_Unknown, _From, State) ->
    {reply, {error, unknown_request}, State}.

handle_cast({put, Key, Value}, State) ->
    {noreply, State#{Key => Value}}.

handle_info(Msg, State) ->
    logger:warning("Unexpected message: ~p", [Msg]),
    {noreply, State}.

terminate(_Reason, _State) -> ok.
…

27.3 Callback reference

{ok, S}, {ok, S, Timeout | hibernate | {continue, C}}, {stop, R}, ignore

{reply, R, S}, {reply, R, S, Timeout}, {noreply, S}, {stop, Reason, R, S} {noreply, S}, {stop, Reason, S} {continue, C} {noreply, S}...

shutdown / stop sys:get_status, crash logs sanitized status map

Start variants: start/3,4, start_link/3,4, start_monitor/3,4. Names: {local, Name}, {global, Name}, {via, Module, Name}.

27.4 Deferred replies

handle_call(slow, From, State) ->
    spawn(fun() -> gen_server:reply(From, compute()) end),
    {noreply, State}.

27.5 Asynchronous requests (OTP 23+)

ReqId = gen_server:send_request(Server, Request),
Reply = gen_server:wait_response(ReqId, 5000),
%% or gen_server:receive_response/2, check_response/2, reqids_* collections (OTP 25+)

27.6 gen_server tips

One message at a time → potential bottleneck; keep callbacks fast.

Default call timeout: 5000 ms.

Never call yourself (deadlock).

Put business logic in pure functions; the gen_server only manages state and messages.