Thank you everyone for sharing!
When talking with some others about this problem before, @MononcQc brought up that there are different levels of configuration, and the different levels have different needs. The levels talked about were:
- Compile-time - How should the code be compiled. This is today mostly solved by rebar3, erlang.mk.
- Boot-time - This is today solved mostly via sys.config and vm args, which relx/cuttlefish/etc augments.
- Run-time - Fetching config from external source at startup or going live-reconfiguration.
We also have different types of products that have different needs. For instance, configuring erlang_ls is very different from configuring a radio base station, which again is very different from configuring dialyzer.
I do not think that Erlang/OTP should include support for doing configuration using an external source as that very quickly becomes a very big problem with many different solutions. I also think that the best way to deal with live-reconfiguration is by doing it the way that logger
does, i.e. the application should provide an API that exposes what can be reconfigured.
There are a couple of things with how we configure at boot-time today that I would like to change.
- Be able to configure
erts
using sys.config
.
For some things command-line arguments are great, but I think that many of the options going into erts
it would be better if they could be configured using sys.config
files.
- Be able to validate configuration and provide user-friendly error reports.
- Allow the application to do the merging of configuration variables, with some good configurable defaults.
- Allow plugging in custom file parsers to parse the config file.
I did a prototype of this last year where you could do the following:
> erl -config sys.config -config sys.toml -erts break 'true' -fdconfig 3 3<<EOF
[{kernel,[{logger_level,alles}]}].
EOF
./sys.config:2 [erts.schedulers.normal.online] invalid value: a. Must be an integer.
./sys.config:3 [erts.schedulers.dirty.cpu.online] invalid value: 11.
Number of online schedulers (11) must be less than or equal to available (8)
./sys.config:5 [erts.schedulers.bind_type] invalid value: default_binding.
Valid values are: [default_bind,no_node_processor_spread,
no_node_thread_spread,no_spread,processor_spread,spread,
thread_spread,thread_no_node_processor_spread,unbound]
./sys.config:6 [erts.schedulers.topo] invalid key: topo.
Valid keys are: [bind_type,dirty,forced_wakeup_interval,load,normal,
port_parallelism,topology,wake_cleanup_threashold,
wakeup_strategy]
./sys.toml: [erts.schedulers.dirty.io.online] invalid value: 10235. Must be 1 =< Value =< 1024.
command line: [erts.break] invalid value: true.
Valid values are: [disable,ignore]
fd 3.config:1 [kernel.logger_level] invalid value: alles.
Valid values are: [emergency,alert,critical,error,warning,notice,info,debug,
all,none]
Much of the validation logic is derived from the typespecs for the options, with some special code needed for when two configuration options depend on each other or some environmental thing.
I stopped working on it because I did not like the solution I had come up with that allowed the user to create custom config parsers, and also the erts
configuration validation was all done in Erlang code which meant that the startup times would suffer.
I’ve now recently started thinking about this again, which is the reason for this post. Configuration is an important part of any software, and I would like to make it easier for developers to provide a great configuration interface to their users (be they end-users or other developers).