couchdb-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Daniel Yokomizo" <daniel.yokom...@gmail.com>
Subject Re: server configuration stuff
Date Thu, 10 Apr 2008 22:52:03 GMT
On Thu, Apr 10, 2008 at 5:57 PM, Jan Lehnardt <jan@apache.org> wrote:
> Heya,
>
>  On Apr 10, 2008, at 22:22, Daniel Yokomizo wrote:
>
> >
> > A couple of thoughts.
> >
> > 1. Why not use the usual Erlang approach for code update, using the
> > loop for receiving messages and handling config updates, it seems
> > cleaner and a better fit. This way you need only a single process to
> > handle the config file format and notify the rest.
> >
>
>  Can you elaborate on that for a bit considering that we're not all
>  Erlang experts here :) Especially, what do you mean be "The Loop"
>  and how is it conceptually different from what Damien proposes
>  in his fourth and fifth paragraph? Do you mean to use the program's
>  main run loop to do the job that Damien describes for a "configuration
>  module"?

In Erlang the usual way to write a process is to create a
tail-recursive non-terminating function:

loop() ->
    receive
        Msg -> handle(Msg),
                   loop()
    end.

To support code updates the usual trick is to create a special kind of
code update function:

loop(Handler) ->
    receive
        {code_update, NewHandler} -> loop(NewHandler);
        Msg -> Handler(Msg),
                   loop(Handler)
    end.

So the loop depends on a function which is the Handler, when it
receives a code_update message it starts using the NewHandler, but for
other messages it let the Handler do the job and loop again using it.

What I suggested was doing something like this:

loop(Config) ->
    receive
        {config_update, NewConfig} -> loop(NewConfig);
        Msg -> handle(Config, Msg),
                   loop(Config)
    end.

Then a configurator process just need to be able to send a message to
all process that use the configuration and send a config_update
message to them with the NewConfig. The configurator process is
external to the modules and can read the configuration from anywhere:
a database, a file.

> > 2. Also it would be simpler to have almost every config inside a
> > CouchDB database, so you startup using the default config and the
> > config process reads the database for the real config info and
> > notifies the other modules of the actual values. With this in place
> > it'll be easier to have other config formats in the future: just make
> > some module that understands format foo and writes/reads it to/from
> > couchdb. The config process will see the changes and propagate them.
> >
>
>  Not sure if it matters where the actual data gets stored. New formats
>  can be added to files as well as CouchDB documents, so one
>  solution is as easy as the other. Or am I missing something?
>
>
> > IME it's better to keep config files away from the core of some
> > application and let some external agent interpret the files and
> > configure the application using an api.
> >
>
>  Sorry to sound dumb again. Do you say keeping the handling
>  of config files should not be in the CouchDB core? What is the
>  core in this context? Would a configuration module that would
>  deal with the config file considered "core" by you? Or would
>  that describe that 'external agent' you mention?

The idea is to pass the config information as Erlang tuples/records to
the processes that need them, so we don't need to know which config
file format was used. Then another process (which can be plugged in)
knows how to parse a config file (or read it from a database) and
handles the parsing, builds a config tuple/record and sends it to the
configurator process, which proceeds to notify all dependent process.


%% init creates a minimal correct DefaultConfig and starts all the
core processes using DefaultConfig,
%% like the couch engine, view engine and the configurator process.
the configurator process starts
%% with a list of processes that it should notify on config updates

init() ->
    DefaultConfig = {...},
    CouchPid = spawn(fun() -> couch(DefaultConfig) end),
    ViewPid = spawn(fun() -> view(DefaultConfig) end),
    ConfigPid = spawn(fun() -> config(DefaultConfig, [CouchPid,ViewPid]) end),
    ConfigPid.

%% couch represents the core CouchDB engine and knows how to handle
config_update messages
couch(Config) ->
    receive
        {config_update, NewConfig} -> couch(NewConfig);
        Msg -> doRealCouchWork(Config, Msg),
                   couch(Config)
    end.

%% view represents the view engine and knows how to handle
config_update messages
view(Config) ->
    receive
        {config_update, NewConfig} -> view(NewConfig);
        Msg -> doRealViewWork(Config, Msg),
                   view(Config)
    end.

%% config is the configurator. all it does is wait for config_update
messages to notify
%% the interested processes
config(Config, Pids) ->
    receive
        {config_update, NewConfig} -> notify(NewConfig, Pids),
            config(NewConfig,Pids)
    end.

notify(NewConfig, []) -> ok;
notify(NewConfig, [Pid|Pids]) ->
    Pid ! {config_update, NewConfig},
    notify(NewConfig, Pids).


Inside a plugin module we could have something like this:

init(ConfigPid) -> spawn(fun() -> loop(ConfigPid) end).
loop(ConfigPid) ->
   File = read_file_blocking_until_it_changes(),
   NewConfig = parse(File),
   ConfigPid ! {config_update, Config},
   loop(ConfigPid).

Then anyone can build it's own config file format (of course we should
have at least one standard format to encourage people to use a common
one), perhaps reading it from a database or whatever and create an
appropriate plugin module that will just do the reading and parsing.
This way we separate the issues of understanding the config file
format and where it's stored, to the notification of interested
processes to the use of the config.

>  Thanks for your input!
>
>  Cheers
>  Jan
>  --

I hope this makes things clearer.

Best regards,
Daniel Yokomizo.

Mime
View raw message