Mnesia のレプリケーションを試す

Mnesia のレプリケーションをいろいろ試してみたのでメモ。

まずは、下記を mnesia_test.erl というファイル名で保存して、コンパイルする。

-module(mnesia_test).
-include_lib("stdlib/include/qlc.hrl").
-record(store, {key, value}).
-export([start/0, start/1, restart/1, set/2, get/1]).

start() ->
  mnesia:start(),
  mnesia:create_schema([node()]),
  mnesia:create_table(store, [{attributes, record_info(fields, store)}]).

start(Nodes) ->
  mnesia:start(),
  mnesia:change_config(extra_db_nodes, Nodes),
  mnesia:add_table_copy(store, node(), ram_copies).

restart(Nodes) ->
  mnesia:start(),
  mnesia:change_config(extra_db_nodes, Nodes).

set(Key, Value) ->
  Row = #store{key=Key, value=Value},
  F = fun() -> mnesia:write(Row) end,
  mnesia:transaction(F).

get(Key) ->
  do(qlc:q([X#store.value || X <- mnesia:table(store), X#store.key =:= Key])).

do(Q) ->
  F = fun() -> qlc:e(Q) end,
  {atomic, Results} = mnesia:transaction(F),
  Results.

次に、erlang ノードを複数起動して動作を確認する。

% erlang -sname a@localhost
(a@localhost)1> mnesia_test:start().
{atomic,ok}
% erlang -sname b@localhost
(b@localhost)1> mnesia_test:start([a@localhost]).
{atomic,ok}
% erlang -sname c@localhost
(c@localhost)1> mnesia_test:start([a@localhost, b@localhost]).
{atomic,ok}

この状態で、どこかのノードに値を設定すると、他のノードから取得できる。

(c@localhost)2> mnesia_test:set(key1, value1).
{atomic,ok}
(a@localhost)2> mnesia_test:get(key1).
[value1]

障害が発生したと仮定して、a ノードと c ノードを落としてみる。

(a@localhost)3> init:stop().
ok
(c@localhost)3> init:stop().
ok

この状態で b ノードから値が取れる事を確認してみる。

(b@localhost)2> mnesia_test:get(key1).
[value1]

最後に、a ノードと b ノードを復帰させて、値が取得できるか確認してみる。

% erl -sname a@localhost
(a@localhost)1> mnesia_test:restart([b@localhost]).
{ok,[b@localhost]}
(a@localhost)2> mnesia_test:get(key1).
[value1]
% erl -sname c@localhost
(c@localhost)1> mnesia_test:restart([a@localhost, b@localhost]).
{ok,[b@localhost, a@localhost]}
(c@localhost)2> mnesia_test:get(key1).
[value1]

便利だなー。

おまけ

erlang の分散機能を使ったお遊びをしたのでメモ。

複数のホストで erlang ノードを起動する。とりあえず手元に imacfreebsd があったのでそれを使った。

% erl -sname a@imac
% erl -sname a@bsd

一応、疎通確認。

(a@imac)1> net_adm:ping(a@bsd).
pong

freebsd には、mnesia_test.beam が存在していないので imac から送る。

(a@imac)2> {Module, Binary, Filename} = code:get_object_code(mnesia_test).
{mnesia_test,<<70,79,82,49,0,0,13,244,66,69,65,77,65,116,111,109,0,0,2,155,0,0,
               0,69,11,109,110,...>>,
             "/path/to/mnesia_test.beam"}
(a@imac)3> rpc:call('a@bsd', code, load_binary, [Module, Filename, Binary]).
{module,mnesia_test}

mnesia_test を試してみる。

(a@imac)4> mnesia_test:start().
(a@bsd)1> mnesia_test:start(nodes()).
{atomic,ok}
(a@bsd)2> mnesia_test:set(key2, value2).
(a@imac)5> mnesia_test:get(key2).
[value2]

とにかくノードさえどこかのホストで起動して、疎通確認さえしておけば、必要な時に必要なだけ Mnesia で利用できる。