release_handler

Serge Aleynikov <>
Fri Jul 28 14:08:06 CEST 2006


Gunilla,

Thank you for looking into this.  I guess it was a misuse of this 
functionality on my part.  Initially I tried this code in order to 
reload configuration in a running node:

reload_config() ->
     case init:get_argument(config) of
     {ok, [ Files ]} ->
         ConfFiles = [begin
                          S = filename:basename(F,".config"),
                          filename:join(filename:dirname(F),
                                        S ++ ".config")
                      end || F <- Files],
         % Move sys.config to the head of the list
         Config = lists:sort(fun("sys.config", _) -> true;
                                (_, _) -> false
                             end, ConfFiles),

         OldEnv = application_controller:prep_config_change(),

         Apps = [{application, A, element(2,application:get_all_key(A))}
                 || {A,_,_} <- application:which_applications()],
         application_controller:change_application_data(Apps, Config),
         application_controller:config_change(OldEnv);
     _ ->
         {ok, []}
     end.

This worked except it didn't remove old environment variables. 
Initially I thought that application_controller:do_change_appl was at 
fault, but later I realized that application:get_all_key/1 among other 
things returns not only the environment read from the *.app file, but a 
merged environment (*.app file + whatever was read from a config file).

Upon realizing that I had to use the following solution to properly 
reload configuration without restarting a node.  This indeed removes the 
*old* environment not found in the new config files.  Perhaps there are 
other ways?

reload_config() ->
     case init:get_argument(config) of
     {ok, [ Files ]} ->
         ConfFiles = [begin
                          S = filename:basename(F,".config"),
                          filename:join(filename:dirname(F),
                                        S ++ ".config")
                      end || F <- Files],
         % Move sys.config to the head of the list
         Config = lists:sort(fun("sys.config", _) -> true;
                                (_, _) -> false end, ConfFiles),

         OldEnv = application_controller:prep_config_change(),

         Apps = [{application, A, make_appl(A)}
                 || {A,_,_} <- application:which_applications()],
         application_controller:change_application_data(Apps, Config),
         application_controller:config_change(OldEnv);
     _ ->
         {ok, []}
     end.

make_appl(App) when is_atom(App) ->
     AppList  = element(2,application:get_all_key(App)),
     FullName = code:where_is_file(atom_to_list(App) ++ ".app"),
     case file:consult(FullName) of
     {ok, [{application, _, Opts}]} ->
         Env = proplists:get_value(env, Opts, []),
         lists:keyreplace(env, 1, AppList, {env, Env});
     {error, _Reason} ->
         lists:keyreplace(env, 1, AppList, {env, []})
     end.

Regards,

Serge

Gunilla Arendt wrote:
> Hi Serge,
> 
> After having been on vacation for a few weeks, I finally got around to
> take a look at this. What happens in do_change_appl/3 is that a new
> "env" for the application is created by merging the configuration
> parameters in the .app file with configuration parameters set for
> the application in the sys.config file and on the command line.
> 
> The call to Mod:config_change/3 isn't made here, but in
> do_config_change/3 where the previous "env" is compared with the new
> "env" as created above:
> 
> ...
> case AppEnvNow of
>     AppEnvBefore ->
>     ok;
>     _ ->
>     case do_config_diff(AppEnvNow, AppEnvBefore) of
>         {[], [], []} ->
>         ok;
>         {Changed, New, Removed} ->
>         case application:get_key(App, mod) of
>             {ok, {Mod, _Para}} ->
>             case catch Mod:config_change(Changed, New,
>                              Removed) of
>                 ok ->
>                 ok;
> ...
> 
> do_config_diff/1 does look for removed parameters, so as far as I can
> see it looks correct.
> 
> Regards,
> Gunilla
> 
> (FYI, I'm going on vacation again and will be back August 14)
> 
> Serge Aleynikov wrote:
>> Thanks, Gunilla!
>>
>> Also, when I was experimenting with the application:config_change/3 it 
>> seemed that the third parameter (Deleted) did not ever get set with 
>> the list of deleted application's environment entries.
>>
>> A closer look into the application_controller:do_change_appl/3 showed 
>> that the environment (NewEnv1, NewEnv2, NewEnv3) would always get 
>> appended/replaced but never deleted:
>>
>> do_change_appl({ok, {ApplData, Env, IncApps, Descr, Id, Vsn, Apps}},
>>            OldAppl, Config) ->
>>     AppName = OldAppl#appl.name,
>>
>>     %% Merge application env with env from sys.config, if any
>>     ConfEnv = get_opt(AppName, Config, []),
>>     NewEnv1 = merge_app_env(Env, ConfEnv),
>>
>>     %% Merge application env with command line arguments, if any
>>     CmdLineEnv = get_cmd_env(AppName),
>>     NewEnv2 = merge_app_env(NewEnv1, CmdLineEnv),
>>
>>     %% included_apps is made into an env parameter as well
>>     NewEnv3 = keyreplaceadd(included_applications, 1, NewEnv2,
>>                 {included_applications, IncApps}),
>>
>>     %% Update ets table with new application env
>>     del_env(AppName),
>>     add_env(AppName, NewEnv3),
>>
>> While you are looking at the previous bug report, please also verify 
>> this one.
>>
>> Regards,
>>
>> Serge
>>
>>
>> Gunilla Arendt wrote:
>>> Hi Serge,
>>>
>>> Yes, the config_change/1 and change_application_data/2 calls should 
>>> be done in the opposite order.
>>>
>>> Note however that release_handler:eval_appup_script/4 is actually not
>>> used by release_handler (the process), but is primarily intended to be
>>> called by the release_handler (the module) functions upgrade_app/2
>>> and downgrade_app/2,3. These two functions were added rather recently
>>> to simplify testing of .appup files. (the release_handler process
>>> calls do_install_release/3 to install a new release.)
>>>
>>> I will correct the bug and try to clarify the documentation.
>>>
>>> Thanks,
>>> Gunilla
>>>
>>>
>>> Serge Aleynikov wrote:
>>>
>>>> OTP team:
>>>>
>>>> I've been studying the code of the sasl's release_handler, and came 
>>>> up with this question that perhaps someone qualified would be able 
>>>> to answer.
>>>>
>>>> As far as I understand, the following functions do the following tasks:
>>>>
>>>> application_controller:prep_config_change()
>>>>   - Fetch OLD applications' environment from
>>>>     application controller's internal ETS table.
>>>>
>>>> application_controller:config_change(EnvBefore)
>>>>   - Notify running applications (via config_change/3 callback) about
>>>>     configuration changes derived from comparing EnvBefore with the
>>>>     content of the application controller's ETS table storing
>>>>     applications' environment.
>>>>
>>>> application_controller:change_application_data(AppSpecs, Config)
>>>>   - Rereads applications' environment from config files stored on disk
>>>>     and stores it to the application controller's ETS table.
>>>>
>>>> Now with this background let's look at how the release handler does 
>>>> the application upgrade:
>>>>
>>>> ==[begin]== sasl/src/release_handler.erl =====
>>>> ...
>>>> eval_appup_script(App, ToVsn, ToDir, Script) ->
>>>>     EnvBefore = application_controller:prep_config_change(),
>>>>     AppSpecL = read_appspec(App, ToDir),
>>>>     Res = release_handler_1:eval_script(Script,
>>>>                     [], % [AppSpec]
>>>>                     [{App, ToVsn, ToDir}],
>>>>                     []), % [Opt]
>>>>     case Res of
>>>>     {ok, _Unpurged} ->
>>>>         application_controller:config_change(EnvBefore),
>>>>         application_controller:change_application_data(AppSpecL,[]);
>>>>     _Res ->
>>>>         ignore
>>>>     end,
>>>>     Res.
>>>> ...
>>>> ==[end]=======================================
>>>>
>>>> Unless I am wrong, from this code it does *not* look like 
>>>> application's config_change/3 callback will ever be invoked by 
>>>> application_controller:config_change(EnvBefore) because EnvBefore 
>>>> has the same applications' environment information as the current 
>>>> content of the applications' controller's ETS table.  It seems to me 
>>>> that at least the config_change/1 and change_application_data/2 
>>>> calls should be done in the opposite order, or better, do something 
>>>> like this:
>>>>
>>>> ...
>>>> {ok, _Unpurged} ->
>>>>     Files =
>>>>         case init:get_argument(config) of
>>>>         {ok, [ ConfFileNames ]} ->
>>>>             [begin
>>>>                 S = filename:basename(F,".config"),
>>>>                 filename:join(filename:dirname(F), S ++ ".config")
>>>>              end || F <- ConfFileNames],
>>>>         {ok, _} ->
>>>>             []
>>>>         end,
>>>>     application_controller:change_application_data(AppSpecL, Files);
>>>>     application_controller:config_change(EnvBefore);
>>>> _Res ->
>>>>     ignore
>>>> ...
>>>>
>>>> I'd appreciate some clarification on the subject.
>>>>
>>>> Regards,
>>>>
>>>> Serge
>>>>
>>>>
>>>
>>>
>>
> 
> 




More information about the erlang-questions mailing list