This section includes examples of
When a functional module has been changed, for example, if a new function has been added or a bug has been corrected, simple code replacement is sufficient, for example:
{"2",
[{"1", [{load_module, m}]}],
[{"1", [{load_module, m}]}]
}.
In a system implemented according to the OTP design principles,
all processes, except system processes and special processes,
reside in one of the behaviours
OTP thus provides no support for changing residence modules except
in the case of
A callback module is a functional module, and for code extensions simple code replacement is sufficient.
Example: When adding a function to
{"2",
[{"1", [{load_module, ch3}]}],
[{"1", [{load_module, ch3}]}]
}.
OTP also supports changing the internal state of behaviour
processes, see
In this case, simple code replacement is not sufficient.
The process must explicitly transform its state using the callback
function
Example: Consider
The
{"2",
[{"1", [{update, ch3, {advanced, []}}]}],
[{"1", [{update, ch3, {advanced, []}}]}]
}.
The third element of the
-module(ch3).
...
-export([code_change/3]).
...
code_change({down, _Vsn}, {Chs, N}, _Extra) ->
{ok, Chs};
code_change(_Vsn, Chs, _Extra) ->
{ok, {Chs, 0}}.
The first argument is
The version is defined by the module attribute
The other callback functions of
Assume that a module is extended by adding an interface function,
as in the example in
If a call is added to this function, say in module
Thus,
{load_module, Module, DepMods}
{update, Module, {advanced, Extra}, DepMods}
Example: The module
myapp.appup:
{"2",
[{"1", [{load_module, m1, [ch3]}]}],
[{"1", [{load_module, m1, [ch3]}]}]
}.
ch_app.appup:
{"2",
[{"1", [{load_module, ch3}]}],
[{"1", [{load_module, ch3}]}]
}.
If instead
{"2",
[{"1",
[{load_module, ch3},
{load_module, m1, [ch3]}]}],
[{"1",
[{load_module, ch3},
{load_module, m1, [ch3]}]}]
}.
In this case, simple code replacement is not sufficient. When a new version of a residence module for a special process is loaded, the process must make a fully qualified call to its loop function to switch to the new code. Thus, synchronized code replacement must be used.
The name(s) of the user-defined residence module(s) must be
listed in the
Example: Consider the example
{ch4, {ch4, start_link, []},
permanent, brutal_kill, worker, [ch4]}
If
{"2",
[{"1", [{update, ch4, {advanced, []}}]}],
[{"1", [{update, ch4, {advanced, []}}]}]
}.
The
-module(ch4).
...
-export([system_code_change/4]).
...
system_code_change(Chs, _Module, _OldVsn, _Extra) ->
{ok, Chs}.
In this case, all arguments but the first are ignored and
the function simply returns the internal state again. This is
enough if the code only has been extended. If instead the
internal state is changed (similar to the example in
The supervisor behaviour supports changing the internal state, that is, changing the restart strategy and maximum restart frequency properties, as well as changing the existing child specifications.
Child processes can be added or deleted, but this is not
handled automatically. Instructions must be given by in
the
Since the supervisor is to change its internal state,
synchronized code replacement is required. However,
a special
First, the new version of the callback module must be loaded,
both in the case of upgrade and downgrade. Then the new return
value of
The following
{update, Module, supervisor}
Example: To change the restart strategy of
-module(ch_sup).
...
init(_Args) ->
{ok, {#{strategy => one_for_all, ...}, ...}}.
The file
{"2",
[{"1", [{update, ch_sup, supervisor}]}],
[{"1", [{update, ch_sup, supervisor}]}]
}.
The instruction, and thus the
{"2",
[{"1", [{update, ch_sup, supervisor}]}],
[{"1", [{update, ch_sup, supervisor}]}]
}.
The changes do not affect existing child processes. For example, changing the start function only specifies how the child process is to be restarted, if needed later on.
The id of the child specification cannot be changed.
Changing the
As stated earlier, changing child specifications does not affect
existing child processes. New child specifications are
automatically added, but not deleted. Child processes are
not automatically started or terminated, this must be
done using
Example: Assume a new child process
{"2",
[{"1",
[{update, ch_sup, supervisor},
{apply, {supervisor, restart_child, [ch_sup, m1]}}
]}],
[{"1",
[{apply, {supervisor, terminate_child, [ch_sup, m1]}},
{apply, {supervisor, delete_child, [ch_sup, m1]}},
{update, ch_sup, supervisor}
]}]
}.
The order of the instructions is important.
The supervisor must be registered as
If the module
{"2",
[{"1",
[{add_module, m1},
{update, ch_sup, supervisor},
{apply, {supervisor, restart_child, [ch_sup, m1]}}
]}],
[{"1",
[{apply, {supervisor, terminate_child, [ch_sup, m1]}},
{apply, {supervisor, delete_child, [ch_sup, m1]}},
{update, ch_sup, supervisor},
{delete_module, m1}
]}]
}.
As stated earlier, the order of the instructions is important.
When upgrading,
Example: A new functional module
{"2",
[{"1", [{add_module, m}]}],
[{"1", [{delete_module, m}]}]
In a system structured according to the OTP design principles,
any process would be a child process belonging to a supervisor, see
When adding or removing an application, no
Restarting an application is useful when a change is too complicated to be made without restarting the processes, for example, if the supervisor hierarchy has been restructured.
Example: When adding a child
{"2",
[{"1", [{restart_application, ch_app}]}],
[{"1", [{restart_application, ch_app}]}]
}.
When installing a release, the application specifications are
automatically updated before evaluating the
{"2", [{"1", []}], [{"1", []}] }.
Changing an application configuration by updating the
Alternatively, application configuration parameters can be
added or updated in
The release handling instructions for adding, removing, and
restarting applications apply to primary applications only.
There are no corresponding instructions for included
applications. However, since an included application is really a
supervision tree with a topmost supervisor, started as a child
process to a supervisor in the including application, a
Example: Assume there is a release containing an application
In a new version of the release, the application
The workflow is as follows:
Step 1) Edit the code for
init(...) ->
{ok, {...supervisor flags...,
[...,
{ch_sup, {ch_sup,start_link,[]},
permanent,infinity,supervisor,[ch_sup]},
...]}}.
Step 2) Edit the
{application, prim_app,
[...,
{vsn, "2"},
...,
{included_applications, [ch_app]},
...
]}.
Step 3) Create a new
{release,
...,
[...,
{prim_app, "2"},
{ch_app, "1"}]}.
The included application can be started in two ways. This is described in the next two sections.
Step 4a) One way to start the included application is to
restart the entire
However, if this is done and a
Instead, a correct
{"B",
[{"A",
[],
[{load_object_code,{ch_app,"1",[ch_sup,ch3]}},
{load_object_code,{prim_app,"2",[prim_app,prim_sup]}},
point_of_no_return,
{apply,{application,stop,[prim_app]}},
{remove,{prim_app,brutal_purge,brutal_purge}},
{remove,{prim_sup,brutal_purge,brutal_purge}},
{purge,[prim_app,prim_sup]},
{load,{prim_app,brutal_purge,brutal_purge}},
{load,{prim_sup,brutal_purge,brutal_purge}},
{load,{ch_sup,brutal_purge,brutal_purge}},
{load,{ch3,brutal_purge,brutal_purge}},
{apply,{application,load,[ch_app]}},
{apply,{application,start,[prim_app,permanent]}}]}],
[{"A",
[],
[{load_object_code,{prim_app,"1",[prim_app,prim_sup]}},
point_of_no_return,
{apply,{application,stop,[prim_app]}},
{apply,{application,unload,[ch_app]}},
{remove,{ch_sup,brutal_purge,brutal_purge}},
{remove,{ch3,brutal_purge,brutal_purge}},
{purge,[ch_sup,ch3]},
{remove,{prim_app,brutal_purge,brutal_purge}},
{remove,{prim_sup,brutal_purge,brutal_purge}},
{purge,[prim_app,prim_sup]},
{load,{prim_app,brutal_purge,brutal_purge}},
{load,{prim_sup,brutal_purge,brutal_purge}},
{apply,{application,start,[prim_app,permanent]}}]}]
}.
Step 4b) Another way to start the included
application (or stop it
in the case of downgrade) is by combining instructions for
adding and removing child processes to/from
Again, the
{"B",
[{"A",
[],
[{load_object_code,{ch_app,"1",[ch_sup,ch3]}},
{load_object_code,{prim_app,"2",[prim_sup]}},
point_of_no_return,
{load,{ch_sup,brutal_purge,brutal_purge}},
{load,{ch3,brutal_purge,brutal_purge}},
{apply,{application,load,[ch_app]}},
{suspend,[prim_sup]},
{load,{prim_sup,brutal_purge,brutal_purge}},
{code_change,up,[{prim_sup,[]}]},
{resume,[prim_sup]},
{apply,{supervisor,restart_child,[prim_sup,ch_sup]}}]}],
[{"A",
[],
[{load_object_code,{prim_app,"1",[prim_sup]}},
point_of_no_return,
{apply,{supervisor,terminate_child,[prim_sup,ch_sup]}},
{apply,{supervisor,delete_child,[prim_sup,ch_sup]}},
{suspend,[prim_sup]},
{load,{prim_sup,brutal_purge,brutal_purge}},
{code_change,down,[{prim_sup,[]}]},
{resume,[prim_sup]},
{remove,{ch_sup,brutal_purge,brutal_purge}},
{remove,{ch3,brutal_purge,brutal_purge}},
{purge,[ch_sup,ch3]},
{apply,{application,unload,[ch_app]}}]}]
}.
Changing code for a program written in another programming language than Erlang, for example, a port program, is application-dependent and OTP provides no special support for it.
Example: When changing code for a port program, assume that
the Erlang process controlling the port is a
init(...) ->
...,
PortPrg = filename:join(code:priv_dir(App), "portc"),
Port = open_port({spawn,PortPrg}, [...]),
...,
{ok, #state{port=Port, ...}}.
If the port program is to be updated, the code for the
code_change(_OldVsn, State, port) ->
State#state.port ! close,
receive
{Port,close} ->
true
end,
PortPrg = filename:join(code:priv_dir(App), "portc"),
Port = open_port({spawn,PortPrg}, [...]),
{ok, #state{port=Port, ...}}.
Update the application version number in the
["2",
[{"1", [{update, portc, {advanced,port}}]}],
[{"1", [{update, portc, {advanced,port}}]}]
].
Ensure that the
1> systools:make_tar("my_release", [{dirs,[priv]}]). ...
Two upgrade instructions restart the emulator:
Intended when ERTS, Kernel, STDLIB, or
SASL is upgraded. It is automatically added when the
Used when a restart of the emulator is required after all
other upgrade instructions are executed.
For more information about this instruction, see
restart_emulator (Low-Level) in
If an emulator restart is necessary and no upgrade instructions
are needed, that is, if the restart itself is enough for the
upgraded applications to start running the new versions, a
simple
{"B",
[{"A",
[],
[restart_emulator]}],
[{"A",
[],
[restart_emulator]}]
}.
In this case, the release handler framework with automatic
packing and unpacking of release packages, automatic path
updates, and so on, can be used without having to specify
From OTP R15, an emulator upgrade is performed by restarting the emulator with new versions of the core applications (Kernel, STDLIB, and SASL) before loading code and running upgrade instruction for other applications. For this to work, the release to upgrade from must include OTP R15 or later.
For the case where the release to upgrade from includes an
earlier emulator version,