Mnesia に保存されたデータを永続化する(後からノードを追加しても大丈夫)

id:cooldaemon:20070919 で書いた Mnesia のレプリケーションの記事で、id:Hamano 氏からコメントで

便利ですよねー、mnesia のレプリケーション
でも最近悩んでいるのが ram_copies <-> ram_copies と disc_copies <-> ram_copies は2つ目を後から起動しても上手くいくのですが disc_copies <-> disc_copies のレプリケーションは一度 mnesia を stop させないとダメっぽいんですよね。
続編に期待しています^^;

とリクエストを頂いたので、disc_copies <-> disc_copies を無停止でやってみました。

まずは、テストで書いたコードを載せます。

-module(mnesia_test).
-record(store, {key, value}).
-export([start/0, start/1, restart/1]).

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

start(Nodes) ->
  mnesia:start(),
  mnesia:change_config(extra_db_nodes, Nodes),
  mnesia:change_table_copy_type(schema, node(), disc_copies),
  mnesia:add_table_copy(store, node(), disc_copies).

restart() ->
  mnesia:start().

いろいろ省いてますが、一応、コンパイルしてすぐ試せるコードになってます。

次に ram_copies 版との違いを挙げます

  • 複製元となるノードでは mnesia:start/0 より先に mnesia:create_schema/1 を実行する
  • 複製先となるノードでは mnesia:change_table_copy_type/3 で schema テーブルをディスクに保存する
  • 複製元も複製先も再起動時には、mnesia:start/0 を実行するだけで OK!!

mnesia:info/0 で見ると、schema もただのテーブルなので、もしやと思って試してみたらビンゴでした(w;

最後に確認を・・・
(hostname -s の実行結果は imac と仮定)

% erl -sname a
(a@imac)1> mnesia_test:start().
{atomic,ok}
% erl -sname b
(b@imac)1> net_adm:ping(a@imac).
pong
(b@imac)2> mnesia_test:start(nodes()).
{atomic,ok}
(b@imac)3> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
store          : with 0        records occupying 279      words of mem
schema         : with 2        records occupying 519      words of mem
===> System info in version "X.X.X", debug level = none <===
opt_disc. Directory "/path/to/Mnesia.b@imac" is used.
use fallback at restart = false
running db nodes   = [a@imac,b@imac]
stopped db nodes   = []
master node tables = []
remote             = []
ram_copies         = []
disc_copies        = [schema,store]
disc_only_copies   = []
[{a@imac,disc_copies},{b@imac,disc_copies}] = [schema,store]
6 transactions committed, 0 aborted, 0 restarted, 2 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok
(b@imac)4>halt().
% erl -sname b
(b@imac)1> net_adm:ping(a@imac).
pong
(b@imac)2> mnesia_test:restart().
ok
(b@imac)3> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
store          : with 0        records occupying 279      words of mem
schema         : with 2        records occupying 519      words of mem
===> System info in version "X.X.X", debug level = none <===
opt_disc. Directory "/path/to/Mnesia.b@imac" is used.
use fallback at restart = false
running db nodes   = [a@imac,b@imac]
stopped db nodes   = []
master node tables = []
remote             = []
ram_copies         = []
disc_copies        = [schema,store]
disc_only_copies   = []
[{a@imac,disc_copies},{b@imac,disc_copies}] = [schema,store]
4 transactions committed, 0 aborted, 0 restarted, 0 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok