Distribution with Mnesia

Martin J. Logan <>
Tue Aug 12 18:59:35 CEST 2003


I have a behaviour that manages this. I wrote it two years ago and have
used it numerous times in production. The behaviour will let you self
seed if there are no other db nodes in you cluster. If there are other
db nodes present then it will automatically replicate with them. 

The file is attached.

Martin


On Tue, 2003-08-12 at 02:52, Ulf Wiger (ÄL2/EAB) wrote:
> Marc,
> 
> If you want to have a very dynamic network of mnesia nodes,
> I suggest you configure a few nodes to hold a disk copy of
> the schema, and manage them with care. Then, you can connect
> other nodes using the mnesia 'extra_db_nodes' feature.
> These nodes will be "diskless" from mnesia's perspective, 
> and may come and go at will... almost. If a diskless node
> loses contact with one of the master nodes, it should 
> unconditionally restart (mnesia, at least).
> 
> Running distributed erlang over SSL is probably a good idea.
> 
> /Uffe
> 
> -----Original Message-----
> From: Marc Ernst Eddy van Woerkom
> [mailto:]
> Sent: Tuesday, August 12, 2003 04:32
> To: 
> Subject: Distribution with Mnesia
> 
> 
> The features of Mnesia, as described in the docs, are remarkable.
> 
> But what exactly is its useful grade of distribution?
> 
> Is it just usuable for a well defined group (not changing too
> much in time) of nodes that are kind of close together (e.g.
> in the same corporate LAN or WAN?
> 
> The other extreme would be a kind of P2P setting,
> with lots nodes that join and leave the net in an 
> unpredictable fashion.
> 
> Or not so extme:
> 
> Would it allow connecting let's say 10 servers
> via the internet?
> Would inter node metadata flow be possible over
> encrypted channels?
> 
> Regards,
> Marc
>  
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File    : fs_db_init.erl
%%% Author  : Martin J. Logan <>
%%%
%%% @doc  
%%%
%%% <p>Brings mnesia databases together dynamically. This module is intended
%%% only to cover the simple cases for replication. The simple cases are defined
%%% as:</p>
%%%
%%% 1. Starting from scratch as the first mnesia db in a replication cluster and 
%%% in this case self seeding the db. <br/>
%%%
%%% 2. Starting with an empty schema and joining a replication cluster.<br/>
%%%
%%% @end
%%%
%%% Created :  1 Feb 2003 by Martin J. Logan <>
%%%-------------------------------------------------------------------
-module(fs_db_init).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------

%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([start_link/1, start_link/2]).

%%--------------------------------------------------------------------
%% Internal  exports
%%--------------------------------------------------------------------
-export([db_init/3]).

%%--------------------------------------------------------------------
%% macro definitions
%%--------------------------------------------------------------------
-define(SERVER, ?MODULE).
-define(DEFAULT_SCHEMA_TYPE, disc_copies).
-define(WAIT_FOR_TABLES, 10000).
 
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%% @spec start_link(CallBackModule) -> {ok, pid()} | {error, Reason}
%% @end
%%--------------------------------------------------------------------
start_link(CallBackModule) ->
    proc_lib:start_link(?MODULE, db_init, [self(), CallBackModule, []]).

%%--------------------------------------------------------------------
%% @doc Starts the server with options.
%% <pre>
%%
%% The options are as follows:
%%  {schema_type, Type}
%%
%% Types:
%%  Type = ram_copies | disc_copies | disc_only_copies
%%
%% </pre>
%% @spec start_link(CallBackModule, Options) -> {ok, pid()} | {error, Reason}
%% @end
%%--------------------------------------------------------------------
start_link(CallBackModule, Options) ->
    proc_lib:start_link(?MODULE, db_init, [self(), CallBackModule, Options]).

%%%=============================================================================
%%% Internal functions
%%%=============================================================================
%%--------------------------------------------------------------------
%% @doc Initialize db then die.
%% @end
%%--------------------------------------------------------------------
db_init(Parent, CallbackModule, Options) ->
    case apply(CallbackModule, init, []) of
        no_init -> 
	    proc_lib:init_ack(Parent, {ok, self()}),
            exit(normal);
        {ok, []} ->
            delete_schema(),
            local_init(CallbackModule, schema_type(Options)),
	    proc_lib:init_ack(Parent, {ok, self()});
        {ok, DBNodes} ->
            delete_schema(),
            remote_init(CallbackModule, schema_type(Options), DBNodes),
	    proc_lib:init_ack(Parent, {ok, self()})
    end.
            
%% deletes a local schema.
delete_schema() ->
    mnesia:stop(),
    mnesia:delete_schema([node()]),
    mnesia:start().
            
%%--------------------------------------------------------------------
%% Locally initialize tables.
%% Returns: void() | exit(Reason)
%%--------------------------------------------------------------------
local_init(CallbackModule, Type) -> 
    ok            = schema_type_specific(Type),
    {ok, Records} = apply(CallbackModule, local_init, []),
    lists:foreach(fun(Record) -> 
			  ok = mnesia:dirty_write(Record) 
		  end, 
		  Records).

%%--------------------------------------------------------------------
%% Join with another node in a mnesia cluster.
%% Types:
%%  Reason = schema_type_change | replication
%%
%% Returns:
%%  ok | {error, Reason} 
%%--------------------------------------------------------------------
remote_init(CallbackModule, Type, []) ->
    {error, replication};
remote_init(CallbackModule, Type, [Node|T]) ->
    case mnesia:change_config(extra_db_nodes, [Node]) of
        {ok, [Node]} -> 
            {ok, TableTypeList} = apply(CallbackModule, remote_init, []),

            Res = mnesia:add_table_copy(schema, node(), Type),
            error_logger:info_msg("fs_db_init:remote_init schema type ~p~n", [Res]),

            Fun = fun({Table, TableType}) ->
                          Res1 = mnesia:add_table_copy(Table, node(), TableType),
                          error_logger:info_msg(
                            "fs_db_init:remote_init add_table copy = ~p~n", [Res1])
		  end,
            lists:foreach(Fun, lists:delete(schema, TableTypeList)),

            Tables = mnesia:system_info(tables),
            case mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES) of
                ok              -> schema_type_specific(Type);
                {error, Reason} -> {error, replication}
            end;
        _ -> 
            remote_init(CallbackModule, Type, T)
    end.
    

%% If the type is disc copy make it so.
%% Returns: ok | {error, schema_type_change}
schema_type_specific(disc_only_copies) ->
        res(mnesia:change_table_copy_type(schema, node(), disc_only_copies));
schema_type_specific(disc_copies) ->
        res(mnesia:change_table_copy_type(schema, node(), disc_copies));
schema_type_specific(_) -> ok.

res({atomic, ok}) -> ok;
res(_)            -> {error, schema_type_change}.
    

%%--------------------------------------------------------------------
%% Determine the storage type of the schema.
%% Types:
%%  Type = ram_copies | disc_copies | disc_only_copies.
%%
%% Returns:
%%  exit({error, {enosuchtype, BadType}}) | Type
%%--------------------------------------------------------------------
schema_type([{schema_type, Type}|T]) -> type(Type);
schema_type([_|T])                   -> schema_type(T);
schema_type([])                      -> ?DEFAULT_SCHEMA_TYPE.

type(ram_copies)       -> ram_copies;
type(disc_copies)      -> disc_copies;
type(disc_only_copies) -> disc_only_copies;
type(BadType)          -> exit({error, {enosuchtype, BadType}}).



















More information about the erlang-questions mailing list