12.Appup Cookbook

Source: Internet
Author: User

This chapter contains an example of a file for a typical run-time upgrade/downgrade case .appup .

Change function Module

When you want to make a change to a functional module, for example if you add a new function or correct an error, replacing it with simple code is sufficient.

For example:

{"2", [{"1", [{Load_module, M}]}], [{"1", [{Load_module, M}]}]}.
Change Resident Module

In systems based on the design principle of OTP, all processes, except system processes and special processes, belong to, supervisor gen_server or are gen_fsm one of gen_event the behaviors. They belong to the Stdlib application, and if an upgrade or downgrade generally requires a simulator reboot.

OTP therefore does not provide support for resident module changes, except for special processes .

Changing the callback module

The callback module is a functional module, which is sufficient for code extensions, as long as simple code substitution is used.

For example: When adding a function to an example in a release process ch3 , you ch_app.appup should:

{"2", [{"1", [{Load_module, CH3}]}], [{"1", [{Load_module, CH3}]}]}.

OTP also supports changing the internal state of the behavior process, see Change internal state below.

Change the internal state

In this case, simple code substitution is not enough. The process must explicitly use the callback function to transform its state before switching to the new version of the callback module code_change . This must be replaced with synchronous code.

For example: Think of the gen_server from the gen_server Act chapter ch3 . The internal state is a value that represents the available channel Chs . Suppose we want to add a counter N to track the current alloc number of requests. This means that we need to change the format to {Chs, N} .

.appupThe file can be:

{"2", [{"1", [{update, CH3, {Advanced, []}}], [{"1", [{update, CH3, {Advanced, []}}]}.

updateThe third element of the directive is a tuple {advanced, Extra} , which says that the affected process is going to make a state transition before loading the new version of the module. The process invokes the callback function code_change (see gen_server(3) ). Extra, where [], is passed to the function as is:

-module (CH3) ....-export ([CODE_CHANGE/3]) .... code_change ({down, _vsn}, {Chs, N}, _extra)    {OK, chs};code_ Change (_VSN, CHS, _extra),    {OK, {CHS, 0}}.

If it is degraded, then the first parameter is {down,Vsn} , if it is an upgrade, then yes Vsn . Expressions Vsn are obtained from the "original" version of the module, that is, which version is upgraded, or which version to downgrade to.

The version is defined by the module properties- vsn -if any. ch3There is no such attribute in, in this case is the checksum of the beam file (a large integer), this value is not interesting, so ignore it here.

( ch3 the other callback functions also need to be modified, and there may be a need to add a new excuse function, which is not shown here).

Module dependencies

Suppose we extend a module by adding a new interface function, as in the case of release processing , where ch3 a function is added available/0 .

Suppose that if we were to m1 add a call to the function in the module, m1 ch3 ch3:available/0 a run-time error would occur if the new version was loaded first and called before the new version was loaded.

Therefore, in the case of an upgrade, it must be loaded before, or vice versa, in the case ch3 m1 of a downgrade. In this way, we say that m1 it is dependent ch3 . In the release processing instructions, this is expressed through DepMods the element:

{load_module, module, depmods} {update, Module, {advanced, Extra}, Depmods}

DepModsis the Module list of modules that are dependent on.

For example, a module in an application myapp m1 is dependent when it is upgraded from "1" to "2" or from "2" to "1" ch3 :

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 m1 and ch3 belong to the same application, the .appup file should be as follows:

{"2", [{"1",   [{load_module, CH3},    {Load_module, M1, [CH3]}]}], [{"1",   [{load_module, CH3},    {Load_ module, M1, [CH3]}]}]}.

Note that when you downgrade, you still m1 depend on it ch3 . systoolsknow the difference between upgrade and downgrade and generate the correct relup one, where it is ch3 m1 loaded before the upgrade, and when it is degraded m1 ch3 .

Code to change special processes

In this case, simple code substitution is not enough. When a new version of a special process's resident module is loaded, the process must make a fully qualified call to its looping function to switch to the new code. This must be replaced with synchronous code.

Note

For special processes, the name of the user-defined resident module must be listed in the child process specification Modules so that the release processor can find the process.

For example. Consider the example in the sys and Proc_lib chapter ch4 . The sub-process specification should be as follows when booting through the path:

{CH4, {CH4, Start_link, []}, permanent, Brutal_kill, worker, [CH4]}

If it ch4 is part of the app, sp_app and when the app is upgraded from version "1" to "2", ch4 a new version to load sp_app.appup should be as follows:

{"2", [{"1", [{update, CH4, {advanced, []}}], [{"1", [{update, CH4, {advanced, []}}]}.

updatedirective must contain a tuple {advanced,Extra} . The directive will let a special process invoke the callback function, which the system_code_change/4 user must implement. The expression Extra , here is [], is passed to system_code_change/4 :

-module (CH4) ....-export ([SYSTEM_CODE_CHANGE/4]) .... System_code_change (Chs, _module, _OLDVSN, _extra),    {OK , CHS}.

The first parameter is an internal state State , from a function sys:handle_system_msg(Request, From, Parent, Module, Deb, State) that is called by a special process when a particular process receives a system message. In ch4 , the internal state is a collection of available channels Chs .

The second parameter is the name of the module ( ch4 ).

The third parameter is Vsn either {down,Vsn} , as described in gen_server:code_change/3 .

Here, all parameters are ignored except the first one, and the function simply returns to the internal state. If the code is just being expanded, then that's enough. If we also want to change the internal state (similar to the example in changing the internal state), we can do it in this function and return it {ok,Chs2} .

Change of the governor's path

Octave to support changing the internal state, that is, changing the restart policy and maximum restart frequency properties, and changing the existing child process specifications.

You can also add and remove child processes, but this is not handled automatically. The instruction must be given in the .appup file.

Change properties

Because the governor needs to change its internal state, it must be replaced with synchronous code. However, special instructions must be used update .

The new version of the callback module must be loaded first, whether in the case of an upgrade or a downgrade. You can then check init/1 the new return value and change the internal state accordingly.

The following directives are used for the Governor upgrade :

{Update, Module, supervisor}

For example, suppose we want to change the restart policy from the Supervisor Behavior Chapter to ch_sup one_for_one one_for_all . We want to modify ch_sup.erl the callback function in init/1 :

-module (Ch_sup) .... Init (_args)    , {OK, {one_for_all, 1, 60}, ...}.

File ch_app.appup :

{"2", [{"1", [{update, CH_SUP, supervisor}]}], [{"1", [{update, CH_SUP, supervisor}]}]}.
Change sub-process specifications

When changing an existing sub-process specification, the directive-including the .appup file-is the same as the change attribute described above:

{"2", [{"1", [{update, CH_SUP, supervisor}]}], [{"1", [{update, CH_SUP, supervisor}]}]}.

The changes do not affect existing child processes. For example, changing the startup function will only specify how the child process should restart if a restart is required later.

Note that the ID of the child process specification can not be changed.

Also note that changing the field of the child process specification Modules may affect the release process itself, because it is used to identify which processes are affected in synchronous code substitution.

Adding and removing child processes

As stated earlier, changing the child process specification does not affect existing child processes. The new sub-process specification will be automatically added but will not be deleted. Also, the child process does not start or terminate automatically, so you must explicitly use the apply directive.

For example: Suppose ch_app we want to ch_sup add a new subprocess when we upgrade from "1" to "2" m1 . This also means that if you downgrade from "2" to "1", m1 you will be removed.

{"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}   ]}]}.

It is important to note the order of instructions.

Also note that the process must be registered as ch_sup the script to function correctly. If the path is not registered, it cannot be accessed directly from the script. This must be done by writing a helper function to find the PID of the path and calling it supervisor:restart_child , and you must use apply the instruction to invoke the function in the script.

If a ch_app module is introduced in the version "2", m1 it must also be loaded during the upgrade and deleted when it is degraded:

{"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}   ]}]}.

It is important to note again the order of instructions. When you upgrade, you must load m1 and change the child process specification of the thread before you can start a new child process. When demoted, the child process must terminate before changing the specification of the child process and deleting the module.

Add or Remove modules

For example, ch_app a new feature module is added m :

{"2", [{"1", [{Add_module, M}]}], [{"1", [{Delete_module, M}]}]
Start or terminate a process

In a system built on the principle of OTP design, any process is a subprocess of a certain path, see Adding and removing child processes earlier.

Add or remove an app

When you add or remove an app, you don't need any .appup files. When generated relup , files are compared .rel and automatically added add_application and remove_application instructed.

Restart your app

When changes are too complex to be done without restarting the process, restarting the application can be useful, for example, when the monitoring hierarchy changes.

For example: When ch_sup adding a new subprocess as m1 shown in The example above , another way to do this is to restart the entire application without updating the process:

{"2", [{"1", [{restart_application, Ch_app}]}], [{"1", [{restart_application, Ch_app}]}]}.
Change Application Specifications

When you install a publication, the application specifications are relup automatically updated before the script is executed. Therefore, no .appup directives are required in the file:

{"2", [{"1", []}], [{"1", []}]}.
Change app Configuration

Changing the .app application configuration by updating the key in the file env is actually a special case of changing the application specification, see the previous section .

Alternatively, you can sys.config add or change application configuration parameters in the file.

Change the included application

The publish processing instructions for adding, deleting, and restarting an app can only be used for the main app. There is no corresponding instruction for the included app. However, because a contained application is actually a supervised tree with the highest supervisor, and is initiated as a subprocess of a certain process in the included application, you can create a relup file manually.

For example: Suppose we have a release that contains an application prim_app , and it has a supervisor tree in it prim_sup .

In the new version of the release, our sample application ch_app is to be included in the prim_app . In other words, its highest path is to be prim_sup started as a child process.

    1. Edit prim_sup The code:
Init (...), {    OK, {... supervisor flags ...,          [...,           {ch_sup, {ch_sup,start_link,[]},            Permanent , Infinity,supervisor,[ch_sup]}}           .
    1. Edited prim_app .app file:
{application, Prim_app, [...,  {VSN, "2"},  ...,  {included_applications, [Ch_app]},  ...]}.
    1. Create a new .rel file that contains ch_app :
{release, ...,  {prim_app, "2"},  {ch_app, "1"}]}.
App Restart

4.a. One way to start a contained app is to restart the entire prim_app app. In general, we want prim_app to .appup use directives in the file restart_application .

However, if we do this and generate a relup file, it will not only contain a reboot (that is, remove and add) prim_app , it will also contain the boot ch_app (which is stopped in the case of demotion). This is because it is ch_app also included in the new .rel file, while the old one is not.

So, the correct   relup   files are created manually, either from scratch or by editing the generated version. Start/stop   the instructions for Ch_app   are replaced with the instructions for loading/unloading the application:

{"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,bruta L_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,sto P,[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_s Up,brutal_purge,brutal_purge}},   {Purge,[prim_app,prim_sup]}, {Load,{prim_app,brutal_purge,brutal_purge}}, {Load,{prim_sup,brutal_purge,brutal_pu Rge}}, {Apply,{application,start,[prim_app,permanent]}]}]}.
Change of the governor's journey

4.b. Another approach is prim_sup to start the application by combining instructions for adding and removing child processes and loading/unloading all ch_app code and application Specifications (stopping in the case of demotion).

This time, you still have to create the relup file manually. You can start writing or editing the generated version. First, before updating prim_sup , load ch_app all the code and load the application specifications. When downgrading, it prim_sup should be updated ch_app before the code and application specifications are unloaded.

{"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]}}]}]}.
Change non-Erlang code

Changing the Code of a program written in a programming language other than Erlang, such as a port program, is closely related to the application, and OTP does not provide special support for this.

For example, change the code of the Port program: Assume that the Erlang process that controls the port is a gen_server portc and that the port is open in a callback function init/1 :

Init (...)    ,    portprg = Filename:join (Code:priv_dir (APP), "PORTC"),    Port = Open_port ({spawn, PORTPRG}, [...]),    ...,    {OK, #state {port=port, ...}}.

If you want to update the port program, then we can extend the Gen_server code, add a close old port and open the new port. (if necessary, Gen_server can request the data to be saved from the port program and then transfer the data to the new port):

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

.appUpdate the app version in the file and write a .appup file:

["2", [{"1", [{update, PORTC, {Advanced,port}}]}], [{"1", [{update, PORTC, {advanced,port}]}]].

Make sure that the directory where the C program resides is priv also included in the new release package:

1> Systools:make_tar ("My_release", [{Dirs,[priv]}]) ....
Simulator Reset

If the emulator needs to be restarted, just create a very simple file manually .relup :

{"B", [{"A", [   ],   [Restart_new_emulator]}], [{"A",   [],   [Restart_new_emulator]}]}.

This allows you to use a publishing processor framework that comes with automatic packaging, unpacking, automatic path updates, and so on, without specifying a .appup file.

If you want to convert some persisted data to a process, such as the database content, before installing a new release, its instructions can be added directly to the .relup file.

12.Appup Cookbook

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.