Archive

Posts Tagged ‘configuration file’

An easy way to handle configuration parameters in Erlang

June 28, 2011 Leave a comment

In one of my previous post (text in Italian, code in English), I showed you how to read an Erlang configuration file and parse it to retrieve a specific value used by your application.

This technique is really easy to implement and allows user to modify on the fly the configuration by changing the configuration file, but unfortunately it introduces also several drawbacks. You should already know that reading/writing files is quite an expensive operation in almost every language around, well Erlang is not excluded; moreover this kind of approach introduces some security problems (e.g. what if the configuration file is corrupted during runtime?).

Improving our configuration

How can we deal configuration in an easy and better way? Well, we know that Erlang processes can share messages in a very fast and effective way, thus we can think of creating a process (in my case a gen_server named mod_conf) which handles requests for configuration parameters.

What should mod_conf do? First of all it should read during its initialization the original configuration file and store the list of tuples in its state, given an atom (i.e. a configuration tag) it should also be able to retrieve from its state the associated value.

Since we can use the nice interface provided by the gen_server behaviour, we may think to add two more functionalities to mod_conf: lookup/1, and update/2. The first of the two functions above will return given a tag the current value stored in the state of the gen_server for that tag, while the second function will update given a tag and a new value the current state store in the state.

Let’s see how we can implement this:

-module(process_conf).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-export([lookup/1, update/2]).

-define(SERVER, ?MODULE).

-record(state, {conf}).

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    {ok, Conf} = file:consult("configuration.cfg"),
    {ok, #state{conf=Conf}}.

handle_call({lookup, Tag}, _From, State) ->
    Reply = case lists:keyfind(Tag, 1, State#state.conf) of
                {Tag, Value} ->
                    Value;
                false ->
                    {error, noinstance}
            end,
    {reply, Reply, State};

handle_call({update, {Tag, Value}}, _From, State) ->
    NewConf = lists:keyreplace(Tag, 1, State#state.conf, {Tag, Value}),
    Reply = ok,
    {reply, Reply, State#state{conf=NewConf}};

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

lookup(Tag) ->
    gen_server:call(?SERVER, {lookup, Tag}).

update(Tag, Value) ->
    gen_server:call(?SERVER, {update, {Tag, Value}}).

So far, so good. Now we can try to benchmark our implementation and see its performances, we can try also to compare the results achieved by using this kind of approach with the results obtained by using a normal configuration file.

-module(test).
-export([start/0]).
-define(CONFTYPE, [file, pserver]).
-define(KEYS, [tag1, tag2, tag3, tag4, tag5, tag6]).
-define(RETRIES, 100000).
-define(KEYNUM, 6).

start() ->
        process_conf:start_link(),
        lists:foreach(fun(ConfType) ->
                                      test(ConfType, 0, ?RETRIES)
                      end, ?CONFTYPE).

test(ConfType, PartialTime, 0) ->
        AvgTime = PartialTime/?RETRIES,
        io:format("Avg-Time: ~pus. Method: ~p.~n", [AvgTime, ConfType]);

test(file, PartialTime, Times) ->
        {Time, _Val} = timer:tc(file_utility,
                                get_conf_value,
                                [lists:nth(random:uniform(?KEYNUM), ?KEYS)]),
        test(file, PartialTime+Time, Times-1);

test(pserver, PartialTime, Times) ->
        {Time, _Val} = timer:tc(process_conf,
                                lookup,
                                [lists:nth(random:uniform(?KEYNUM), ?KEYS)]),
        test(pserver, PartialTime+Time, Times-1).

In order to run the previous test you must create and compile another module (file_utility) which exports the following function:

get_conf_value(What) ->
    {ok, Conf} = file:consult("configuration.cfg"),
    case lists:keyfind(What, 1, Conf) of
        {What, Value} ->
            Value;
        false ->
            {error, noinstance}
    end.

Ok, time for testing!

paolo@paolo-laptop:~/Desktop/various/test$ erl
Erlang R13B03 (erts-5.7.4)
Eshell V5.7.4  (abort with ^G)
1> test:start().
Avg-Time: 728.07486us. Method: file.
Avg-Time: 9.8088us. Method: pserver.
ok
2>

Fair enough, the behaviour of our implementation seems much faster..but in fact this implementation is not totally safe! Using only one process to handle configuration may create a bottleneck in case of a huge amount of requests, therefore if you are in this situation if preferable to use a process pool.

Ok, this is all for today, but before the conclusion of this post I would like to say one thing more: this is just a really simple demo on how things can be implemented to improve your systems, but other ways are possible in erlang…I suggest you to read this post on stackoverflow to get a better insight on the topic!

Configuration file in Erlang

September 26, 2009 1 comment

Quando scrivete un progetto in erlang, grande o piccolo che sia, vi troverete davanti alla necessità di spostare con il codice hard-coded in un foglio di configurazione, che l’utente avrà la possibilità di modificare a piacere.

In erlang tutto ciò è molto semplice, tanto da poter essere implementato in poche righe di codice.

Iniziamo creando un file di configurazione che metteremo nella directory di Unix /etc/software/software.cfg

Possiamo creare tale file usando il comando vim dalla shell di Unix, oppure un qualsiasi editor di testo (nota bene che la cartella /etc/ è di solito di proprietà dell’utente root, quindi o eseguite tali comandi come tale utente, o cambiate directory con una vostra directory locale.

pegaso:~ bellerofonte$ sudo mkdir /etc/software
pegaso:~ bellerofonte$ sudo vim /etc/software/software.conf

E qui editate il file aggiungendo i parametri che vi pare (i miei rappresentano il mio blog), seguendo però la seguente forma:

{nome, "Paolo D'Incau's Blog"}.
{url, "http://pdincau.wordpress.com/"}.
{framework, "WordPress"}.

Ogni campo viene identificato con una tupla a due campi: il primo è un atom che identifica la tupla (tag), mentre il secondo è il valore associato a tale tag e può essere un qualsiasi tipo erlang (lista, tupla, atom etc etc).

E’ importante che mettiate il punto alla fine di ogni riga e che controlliate che ogni tupla sia corretta nella sintassi.

Ora come carichiamo tali tali nel nostro codice erlang?

Ecco la risposta:

>1 {ok, Conf} = file:consult("/etc/software/software.cfg").
{ok,[{nome,"Paolo D'Incau's Blog"},
     {url,"http://pdincau.wordpress.com/"},
     {framework,"WordPress"}]}

Come vedete il risultato della funzione consult/1 del modulo file è una tupla. Nel caso tutto sia giusto, tale tupla sarà composta dall’atom ok e dalla lista dei valori nel file di configurazione.

Io metto {ok, Conf} confidente nel fatto che facciate tutto giusto, ma se cosi non fosse otterreste questo errore:

>1 {ok, Conf} = file:consult("/etc/software/software.cfg").
** exception error: no match of right hand side value {error,enoent}

Ok, ora come recuperiamo tra questi valori un singolo valore che ci interessa?
Ecco una possibile soluzione:

4> lists:keysearch(nome, 1, Conf).
{value,{nome,"Paolo D'Incau's Blog"}}

In questo caso ho utilizzato la funzione keysearch/3 del modulo lists. Essa prende in ingresso, una tag, un intero (che rappresenta quale posizione della tupla deve avere tale tag) e una lista di tuple.

Categories: Erlang Tags: , , , ,
Follow

Get every new post delivered to your Inbox.