環境
- Mac OS X 10.9.4
- Erlang/OTP 17.1
概要
Erlang VM を複数立てて、Mnesia の同期テストを CommonTest ではなく EUnit ではどうやるのか調べていたところ slave モジュールを使えばよいという事だったので、slave モジュールを使ってみることにした。
CommonTest には ct_slave という専用モジュールがある模様。
フロー
- ノード名を自分に付けます
- net_kernel:start/1
- クッキーをセットします
- erlang:set_cookie/2
- ノードを 3 つ起動します
- slave:start/3
- -setcookie でクッキーを指定する
- 3 ノード全てで mnesia を起動します
- mnesia:start/0
- 1 ノードに対してテーブルを生成します
- mnesia:create_table/2
- テーブルを作成したノードに対して他のノードを追加します
- mnesia:change_config/2
- テーブルを作成したノードに対してレプリカ対象のテーブルを追加します
- mnesia:add_table_copy/3
- テーブルを作成したノードに bacon というキーでカウンターを +1 します
- mnesia:dirty_update_counter/3
- 他のノードに対してカウンターの値を取得します
- mnesia:dirty_read/2
- 全てのノードでカウンターを増加させる
- 起動した 3 ノードを終了します
- slave:stop/1
- 自分のノード名を削除します
- net_kernel:stop/0
サンプル
説明
- ノードは 3 台
- データベースはオンメモリ
- 同期は Mnesia 独自を使用
コード
-module(spam).
-export([main/0]).
-include_lib("eunit/include/eunit.hrl").
-record(store, {key :: binary(),
value :: non_neg_integer()}).
main() ->
{ok, _Pid} = net_kernel:start([spam, shortnames]),
true = erlang:set_cookie(node(), spam),
Host = list_to_atom(net_adm:localhost()),
Args = "-setcookie spam",
{ok, N1} = slave:start(Host, n1, Args),
{ok, N2} = slave:start(Host, n2, Args),
{ok, N3} = slave:start(Host, n3, Args),
rpc:call(N1, net_kernel, connect_node, [N2]),
rpc:call(N1, net_kernel, connect_node, [N3]),
rpc:call(N1, mnesia, start, []),
rpc:call(N2, mnesia, start, []),
rpc:call(N3, mnesia, start, []),
rpc:call(N1, mnesia, create_table, [store, [{attributes, record_info(fields, store)}]]),
rpc:call(N1, mnesia, change_config, [extra_db_nodes, node()]),
[ rpc:call(N1, mnesia, add_table_copy, [store, N, ram_copies]) || N <- nodes() ],
1 = rpc:call(N1, mnesia, dirty_update_counter, [store, <<"bacon">>, 1]),
[{store, <<"bacon">>, 1}] = rpc:call(N2, mnesia, dirty_read, [store, <<"bacon">>]),
[{store, <<"bacon">>, 1}] = rpc:call(N3, mnesia, dirty_read, [store, <<"bacon">>]),
2 = rpc:call(N2, mnesia, dirty_update_counter, [store, <<"bacon">>, 1]),
[{store, <<"bacon">>, 2}] = rpc:call(N1, mnesia, dirty_read, [store, <<"bacon">>]),
[{store, <<"bacon">>, 2}] = rpc:call(N3, mnesia, dirty_read, [store, <<"bacon">>]),
3 = rpc:call(N1, mnesia, dirty_update_counter, [store, <<"bacon">>, 1]),
4 = rpc:call(N2, mnesia, dirty_update_counter, [store, <<"bacon">>, 1]),
5 = rpc:call(N3, mnesia, dirty_update_counter, [store, <<"bacon">>, 1]),
slave:stop(N1),
slave:stop(N2),
slave:stop(N3),
ok = net_kernel:stop(),
ok.
spam.erl で保存して、試せます。
$ erl
Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Eshell V6.1 (abort with ^G)
1> c(spam).
{ok,spam}
2> spam:main().
ok
Discussion