Archive

Posts Tagged ‘Mnesia’

Erlang hints

June 7, 2010 Leave a comment

Erlang is a wonderful tool…but sometimes it is awkward to manage your stuff (processes or ets/mnesia tables) just by using the shell. Let’s see how to exploit Erlang modules to make our lifes easier!

Many times I was looking for a specific Erlang process using one of the bifs you can see below:

1> registered().
[init,global_group,erl_prim_loader,user,error_logger,rex,
 standard_error_sup,kernel_sup,global_name_server,inet_db,
 file_server_2,code_server,user_drv,application_controller,
 standard_error,kernel_safe_sup]

Or

2> processes().
[<0.0.0>,<0.3.0>,<0.5.0>,<0.6.0>,<0.8.0>,<0.9.0>,<0.10.0>,
 <0.11.0>,<0.12.0>,<0.13.0>,<0.14.0>,<0.15.0>,<0.16.0>,
 <0.17.0>,<0.18.0>,<0.19.0>,<0.20.0>,<0.21.0>,<0.22.0>,
 <0.23.0>,<0.24.0>,<0.25.0>,<0.26.0>,<0.27.0>,<0.28.0>,
 <0.34.0>]

For me it can be painful to use this kind of approach, so I geeked around to search some new ways to follow processes; here are two useful modules I found.

The first one is etop, a module which works pretty much as the unix application etop.

4> etop:start([{output, text}]).  

========================================================================================
 nonode@nohost                                                             08:26:31
 Load:  cpu         0               Memory:  total        3961    binary         13
        procs      27                        processes     582    code         1888
        runq        0                        atom          294    ets           132

Pid            Name or Initial Func    Time    Reds  Memory    MsgQ Current Function
----------------------------------------------------------------------------------------
<0.3.0>        erl_prim_loader          '-'  265995   23512       0 erl_prim_loader:loop
<0.19.0>       code_server              '-'  109125   71224       0 code_server:loop/1
<0.34.0>       etop_server              '-'    7164   21072       0 etop:data_handler/2
<0.6.0>        application_controll     '-'    6931  212992       0 gen_server:loop/6
<0.26.0>       erlang:apply/2           '-'    5151   17184       0 shell:shell_rep/4
<0.0.0>        init                     '-'    2501    8316       0 init:loop/1
<0.25.0>       group:server/3           '-'    1820   12304       0 group:server_loop/3
<0.10.0>       kernel_sup               '-'    1289    6096       0 gen_server:loop/6
<0.23.0>       user_drv                 '-'    1278    9308       0 user_drv:server_loop
<0.27.0>       proc_lib:init_p/5        '-'     268    1372       0 gen_server:loop/6
========================================================================================

The second is the bif regs():

2> regs().

** Registered procs on node nonode@nohost **
Name                  Pid          Initial Call                      Reds Msgs
application_controlle <0.6.0>      erlang:apply/2                     444    0
code_server           <0.19.0>     erlang:apply/2                  104722    0
erl_prim_loader       <0.3.0>      erlang:apply/2                  167826    0
error_logger          <0.5.0>      gen_event:init_it/6                226    0
file_server_2         <0.18.0>     file_server:init/1                  86    0
global_group          <0.17.0>     global_group:init/1                 60    0
global_name_server    <0.12.0>     global:init/1                       53    0
inet_db               <0.16.0>     inet_db:init/1                     130    0
init                  <0.0.0>      otp_ring0:start/2                 2450    0
kernel_safe_sup       <0.28.0>     supervisor:kernel/1                 57    0
kernel_sup            <0.10.0>     supervisor:kernel/1               1289    0
rex                   <0.11.0>     rpc:init/1                          36    0
standard_error        <0.21.0>     standard_error:server/2              7    0
standard_error_sup    <0.20.0>     supervisor_bridge:standar           40    0
user                  <0.24.0>     group:server/3                      38    0
user_drv              <0.23.0>     user_drv:server/2                  625    0

** Registered ports on node nonode@nohost **
Name                  Id              Command
ok

The third is pman, a graphical process manager very useful to inspect the Erlang processes executing either locally or on remote nodes. It is also possible to trace events in the individual processes.

3> pman:start().

pman on windows7

And that was for processes! But, since many times you have also to work with Mnesia/ets tables, I think that this can be also useful to you. Let’s say you want to see what tables you have in your system, in this case you will probably use:

1> ets:all().
[8207,4110,13,file_io_servers,inet_hosts_file_byaddr,
 inet_hosts_file_byname,inet_hosts_byaddr,inet_hosts_byname,
 inet_cache,inet_db,global_pid_ids,global_pid_names,
 global_names_ext,global_names,global_locks,ac_tab]

Well. I suggest you to use tv, that graphically examines ETS and Mnesia tables

tv on windows7

That was all! Stay tuned!

Advertisements
Categories: English, Erlang Tags: , ,

Suffering from amnesia? Use Mnesia!

May 31, 2010 Leave a comment

As you should already know, I’m currently working with Erlang and exmpp to build some XMPP gateways: some of them require only the Jid (jabber id) of the user, while on the other hand some require more specific information as the credentials needed to retrieve user’s personal information from the internet (e.g. Twitter ID and password).

Actually I could use OAuth and ask the user only for his id, but I thought to keep it simple and to go for the full credentials, so the point now is: how and where should I store these data? Well, MySQL  is definitely among the best solutions if you have to store a wide amount of data, but since in my case this is not true, I decided to focus my attention on Mnesia, a distributed DataBase Management System used in Erlang applications, which require continuous operation and exhibit soft real-time properties.

The first thing to do is to create a schema, a sort of description of your databas (e.g. are the data stored on RAM memory? Disk memory? Both?).

When you create a new Mnesia schema, you just save an empty schema table that you will populate further.

To create the schema for you application, start your distributed Erlang nodes and connect them. If you don’t want to distribute Mnesia over all the nodes, just start a non-distributed Erlang node (be careful! It is important that no old schemas exist, as well as ensuring that Mnesia is NOT started).
Ok let’s see same code!

Let’s define the record user_info as:

-record(user_info, {jid, presence, uid, pwd}).

In the init([]) function we add the following code:

case mnesia:create_schema([node()]) of
  ok ->
    ok = mnesia:start(),
    {atomic,ok}=mnesia:create_table(user_table,
                                    [{disc_copies, [node()]},
 				     {type, set},
 				     {attributes, record_info(fields, user_info)}]),
    mnesia:add_table_index(user_table, presence);
  _ ->
    mnesia:start()
end,

As you can see we try to create a new schema by calling the function  mnesia:create_schema(Nodes) that has to be executed on one of the connected nodes, if you want to set up the schema only in the local node just let the command as is it in the previous example, otherwise put as argument of the function:  [node()|nodes()].

If the creation was successful, we start the application by calling mnesia:start() or application:start(mnesia) (note that to stop Mnesia you call either application:stop(mnesia) or mnesia:stop()).

After the creation of the schema, we create the first table (user_table). The first parameter of the function is of course the name, followed by a list of characteristics for the table. First of all we provide the list of nodes where we want disc and RAM replicas of the table, then we state that the table is a set (other possible values are bag and ordered_set), in the end we specify the fields of the table, by saying that they are the ones of the record user_info we declared above.

After this, we add another index to the table for our inspections to the table (remember that the first attribute is always an index.

In worths to notice when the init function starts, if the schema is already there, we just start Mnesia.

One of the coolest feature of Mnesia are transactions:  a way to prevent race conditions while touching the database, anyhow in this post I’m not gonna talk about transactions, since I’m going to discuss about dirty operations, that are not good for preventing race  conditions, but are 10 times faster.

Here there are two functions, let’s analyze them:

set_subscribtion(BareFrom, UId, Pwd) ->
    mnesia:dirty_write({user_table, BareFrom, "online", UId, Pwd}).

set_subscription takes as input 3 values: a Jid, a User id and a Password, dirty_write is used to write into the specified tablea (user_table) these values.

get_subscribtion(BareFrom) ->
   case mnesia:dirty_read({user_table, BareFrom}) of
       [] ->
	   notsubscribed;
       [{user_table, BareFrom, _Status, UId, Pwd}] ->
	   {subscribed, UId, Pwd}
   end.

get_subscription takes as input only the Jid and checks whether the user identified by it is registered to our service, in fact if the dirty_read is an empty list it means that the user in not subscribed, otherwise the user data will be returned. Note that to use dirty read you must have the relative attribute as index (in this case we used jid, but we can use also presence).