[erlang-questions] Use of application environment

Serge Aleynikov saleyn@REDACTED
Wed Dec 3 14:01:17 CET 2008


Edwin Fine wrote:
> I assume, reading the code you linked to, that one still needs to include in
> the config_change callback something to propagate the changes to the various
> workers and supervisors. Do you think that the subscribe/notify mechanism I
> described is workable?

Frankly, I think this is a bit over-engineered, as you'd need to deal 
with re-subscription in every worker.  Since the config_change event 
gives you a reactivity callback to configuration changes, the rest that 
you need is a "standard" way to expose configuration change requests in 
every worker.  One other approach would be to implement the following 
two functions in each worker process:

     options() -> [Param::atom()].
     reconfig(AddedOptions, ChangedOptions, DeletedOptions) -> Result
         Result  = ok | {error, Reason}
         AddedOptions = ChangedOptions = DeletedOptions =
             [{Param::atom(), Value}]

or perhaps, just:

     options() -> [Param::atom()].
     reconfig(Options) -> ok | {error, Reason}
         Options = [{Param::atom(), Value}]

Then the application's config_change/3 callback would just go through 
the list of modules in the app and do:

Options = ...,
Modules = ...,
lists:foreach(fun(M) ->
	Opts = [O || O <- Options, lists:member(O, M:options())],
         M:reconfig(Opts)
end, Modules).

> 
>> This is a simplistic approach that doesn't involve configuration
>> validation.  Actually this was a part of a configuration management
>> framework that we developed at my former employer that included a
>> configuration server that maintained the versioned configuration repository
>> that also performed syntax verification based on the application's metadata
>> deployed with each app that included all needed parameters for each
>> environment type (e.g. prod/dev/qa/etc), parameter types, defaults, update
>> actions (such as reload environment, restart application, restart node,
>> etc.), parameter documentation.  The server would perform configuration
>> validation based on this metadata and reject unconformable input.  Each node
>> would run a configuration client that upon startup would query the
>> configuration server and rebuild the local configuration file if there were
>> any parameter changes.  It would then use that configuration file in
>> starting a release.
> 
> That's pretty sophisticated and along the lines of what I had envisioned.
> It's a pity I don't have the time to write something like that. Your former
> employer was evidently unlucky to lose you :)

Given the need to support a family of three kids and the state of 
telecom at a time the switch to financial industry was quite 
indispensable... ;-)

> Do you by any chance remember if the metadata was able to describe complex
> terms involving nested lists and tuples? Also, do you know of something like
> type_of(Term) (besides is_integer/1 and the others) available somewhere in
> Erlang?

Yes, the meta parameter info was in the form:

%%% @type param_spec() = ParamSpec
%%%           ParamSpec = {Param,Desc,TypeSpec,Default,Required,Restart}
%%%           Param = atom()
%%%           Desc = string()
%%%           TypeSpec = expected_type()
%%%           Default = term()
%%%           Required = true | false
%%%           Restart = reload | application | restart | reboot

And specification language for expected_type() was something like 
(incomplete):

%%% @type expected_type() = ExpectedType
%%%          ExpectedType = atom | {value, ValidItems::list()} |
%%%             tuple | boolean |string |ip_address |ip_port |ip_tuple |
%%%             integer | {integer, Min::integer(), Max::integer()} |
%%%             float   | {float, Min::float(), Max::float()} |
%%%             list    | {list, ValidItems::list()} | {list_of, Type} |
%%%             {func, FunStr::string()} |
%%%             {func, TypeDescBin::binary(), FunStr::string()}
%%%          Type = integer | float | atom | list | tuple | ip_address |
%%%                 ip_port | number | string | ValidItems | node |
%%%                 TupleType
%%%          TupleType = { ExpectedType }
%%%          Fun = (Opt, Value) -> ok |
%%%                    throw({error, {bad_config, {Key, Val}, Reason}})
%%%          ValidItems = list()

So an example of meta parameter definition would be:
[{param1, <<"Docs for param1">>, {integer, 0, 100}, 0, true, reload},
  {param2, <<"Docs for param2">> , string,  "",  true, reload},
  {param3, <<"Docs for param3">> , {list, [one,two,three]}, [one], true, 
reload},
  {param4, <<"Name/Val List">>, {list_of, [{{value, [one]}, string}, 
{{value, [two]}, integer}]}, [],  true, reload}],

Note that the param3 can take a value of 'one', 'two' or 'three'.  And 
the param4 can take a list of name/value pairs, such as:
   [{one, "example"}, {two, 2}]

Regards,

Serge



More information about the erlang-questions mailing list