2022/01/03

Erlang Appup Cookbook

.appup applicaiton upgrade files 可支援系統動態更新,裡面描述如何 upgrade/downgrade a running system,這個檔案會被 systools 的 relup 使用,產生 release upgrade file

application.appup

applicaiton.appup 必須要出現在 application 的 ebin 目錄

該檔案內容只包含一個 erlang term,定義 upgrade/downgrade application 的動作

{Vsn,
  [{UpFromVsn, Instructions}, ...],
  [{DownToVsn, Instructions}, ...]}.
% 目前 application version
Vsn = string()

% 更新自哪一版 application version
UpFromVsn = string() | binary()

% 降版時,要降成哪一個 application version
% 可使用 binary regex
% 例如 <<"2\\.1\\.[0-9]+">> 就是 2.1.*
DownToVsn = string() | binary()

% release upgrade instructions
Instructions

Release Upgrade Instructions

分為 high-level/low-level instructions,建議使用 high-level,systool 會自動產生 low-level instructions

high-level instructions

{update, Mod}
{update, Mod, supervisor}
{update, Mod, Change}
{update, Mod, DepMods}
{update, Mod, Change, DepMods}
{update, Mod, Change, PrePurge, PostPurge, DepMods}
{update, Mod, Timeout, Change, PrePurge, PostPurge, DepMods}
{update, Mod, ModType, Timeout, Change, PrePurge, PostPurge, DepMods}
  Mod = atom()
  ModType = static | dynamic
  Timeout = int()>0 | default | infinity
  Change = soft | {advanced,Extra}
    Extra = term()
  PrePurge = PostPurge = soft_purge | brutal_purge
  DepMods = [Mod]

所有 processes 都會被 sys:suspend 暫停,載入新版 module 後,以 sys:resume 繼續執行

  • Change

    預設為 soft,定義 type of code change

    設定為 {advanced, Extra} 時,以 gen_server, gen_fsm, gen_statem, gen_event 時做的 processes,會呼叫 code_change 改變內部狀態, Extra 為參數

  • PrePurge

    預設為 brutal_purge

    載入新版 code 以前,控制執行舊版 code 的 processes 的動作

    brutal_purge 就是把 process killed

    soft_purgerelease_handler:install_release/1 returns {error,{old_processes,Mod}}.

  • PostPurge

    預設為 brutal_purge

    載入新版 code 時,控制執行舊版 code 的 processes 的動作

    brutal_purge,當 release 確認永久生效時,code 被 purged,process 被 killed

    soft_purge,在沒有 process 執行舊版的 code 時,release handler 會 purges old code

  • DepMods

    預設為 []

    定義 Mod 跟哪些 modules 相依

    在 upgrading/downgrading 時,DepMods 的 processes,會被 suspended

  • Timeout

    定義 suspending processes 的 timeout 時間

  • ModType

    預設為 dynamic

    dynamic 表示 process 會自動使用新版的 code

    static,process 被詢問是否要 change code 以前,就會載入新版 code

supervisor 的 update,是用來修改 start spec


  • 更新 module Mod
{load_module, Mod}
{load_module, Mod, DepMods}
{load_module, Mod, PrePurge, PostPurge, DepMods}
  Mod = atom()
  PrePurge = PostPurge = soft_purge | brutal_purge
  DepMods = [Mod]
  • 載入新的 module Mod
{add_module, Mod}
{add_module, Mod, DepMods}
  Mod = atom()
  DepMods = [Mod]
  • 刪除 module
{delete_module, Mod}
{delete_module, Mod, DepMods}
  Mod = atom()
  • 新增 application
{add_application, Application}
{add_application, Application, Type}
  Application = atom()
  Type = permanent | transient | temporary | load | none
  • 刪除 application
{remove_application, Application}
  Application = atom()
  • restart application
{restart_application, Application}
  Application = atom()

example

{“0.2.0”,
  [{“0.1.0”, [
              {add_module, state_handler}
             ,{update, nine9s_sup, supervisor}
             ,{apply, {supervisor, restart_child, [nine9s_sup, state_handler]}}
             ,{load_module, default_handler}
             ,{add_module, count_handler}
             ,{load_module, nine9s_app}
             ,{apply, {nine9s_app, set_routes_new, [] }} ] }],
  [{“0.1.0”, [
              {load_module, default_handler}
             ,{apply, {supervisor, terminate_child, [nine9s_sup, state_handler]}}
             ,{apply, {supervisor, delete_child, [nine9s_sup, state_handler]}}
             ,{update, nine9s_sup, supervisor}
             ,{delete_module, state_handler}
             ,{apply, {nine9s_app, set_routes_old, [] }}
             ,{delete_module, count_handler}
             ,{load_module, nine9s_app}
             ]
}]}.

upgrade

  • {add_module, state_handler}
    • 增加 state_handler module
  • {update, nine9s, supervisor}
    • 修改 nine9s supervisor 內部狀態 及 spec
  • {apply, {supervisor, restart_child, [nine9s, state_handler]}}
    • apply 會執行 M:F(A1, … An)
    • 也就是執行 supervisor:restart_child(nine9s, state_handler)
  • {load_module, default_handler}
    • 載入 default_handler module,取代舊版的 code
  • {add_module, count_handler}
    • 增加 count_handler module
  • {load_module, nine9s_app}
    • 載入 nine9s_app module
  • {apply, {nine9s_app, set_routes_new, [ ] }} ] } ]
    • 執行 nine9s_app:set_routes_new()

downgrade

  • {load_module, default_handler}
    • 載入舊版的 default_handler
  • {apply, {supervisor, terminate_child, [nine9s_sup, state_handler]}}
    • 停止 state_handler process
  • {apply, {supervisor, delete_child, [nine9s_sup, state_handler]}}
    • nine9s_sup 裡面刪除 state_handler
  • {update, nine9s_sup, supervisor}
    • 修改 nine9s_sup 的內部狀態
  • {delete_module, state_handler}
    • 删除 state_handler module
  • {apply, {nine9s_app, set_routes_old, [ ] }}
    • 設定舊版的 routes
  • {delete_module, count_handler}
    • 刪除 count_handler module
  • {load_module, nine9s_app}
    • 載入舊版的 nine9s_app module

example of appup

  • 更新 functional module

一般功能性的 module 修改後,都可以直接更新

{"2",
 [{"1", [{load_module, m}]}],
 [{"1", [{load_module, m}]}]
}.
  • 修改常駐 module

除了 system processes, special processes 以外,常駐 module 就是supervisor, gens_server, gen_fsm, gen_statem, gen_event 其中之一。

  • 修改 callback module

跟一般功能性的 module 修改一樣

{"2",
 [{"1", [{load_module, ch3}]}],
 [{"1", [{load_module, ch3}]}]
}.
  • 修改 internal state

process 需要呼叫 code_change

{"2",
 [{"1", [{update, ch3, {advanced, []}}]}],
 [{"1", [{update, ch3, {advanced, []}}]}]
}.
-module(ch3).
...
-export([code_change/3]).
...
% downgrade 時呼叫
code_change({down, _Vsn}, {Chs, N}, _Extra) ->
    {ok, Chs};
% upgrade 時呼叫
code_change(_Vsn, Chs, _Extra) ->
    {ok, {Chs, 0}}.
  • module dependencies
{load_module, Module, DepMods}
{update, Module, {advanced, Extra}, DepMods}

m1 跟 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}]}]
}.
{"2",
 [{"1",
   [{load_module, ch3},
    {load_module, m1, [ch3]}]}],
 [{"1",
   [{load_module, ch3},
    {load_module, m1, [ch3]}]}]
}.
  • 修改 special process 的 code

ch4 in proc_lib 由 supervisor 啟動, child spec 為

{ch4, {ch4, start_link, []},
 permanent, brutal_kill, worker, [ch4]}

ch4 是 sp_app 的其中一個 application,在更新 1-> 2 時,必須要載入新版的 module

{"2",
 [{"1", [{update, ch4, {advanced, []}}]}],
 [{"1", [{update, ch4, {advanced, []}}]}]
}.
-module(ch4).
...
-export([system_code_change/4]).
...

system_code_change(Chs, _Module, _OldVsn, _Extra) ->
    {ok, Chs}.
  • 更新 supervisor

可修改 restart strategy 與 maximum restart frequency property

{update, Module, supervisor}
{"2",
 [{"1", [{update, ch_sup, supervisor}]}],
 [{"1", [{update, ch_sup, supervisor}]}]
}.

修改 child spec

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

add/delete child processes

{"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}
   ]}]
}.
{"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}
   ]}]
}.
  • add/delete module
{"2",
 [{"1", [{add_module, m}]}],
 [{"1", [{delete_module, m}]}]
  • start/terminate a process

  • add/remove application

  • restart application

{"2",
 [{"1", [{restart_application, ch_app}]}],
 [{"1", [{restart_application, ch_app}]}]
}.
  • 修改 application spec
{"2",
 [{"1", []}],
 [{"1", []}]
}.
  • 修改 application configuration

也就是更新 sys.config

  • 修改 included applications

  • 修改 non-erlang code

  • emulator restart and upgrade

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

References

Appup

Appup Cookbook

用Rebar3热更新Erlang代码

Appup Cookbook cn

沒有留言:

張貼留言