🤩

Mnesia の同期テストを slave モジュール使って行う

3 min read

環境

  • macOS X 11.4
  • Erlang/OTP 24.0

概要

Erlang VM を複数立てて、Mnesia の同期テストを 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),

    true = rpc:call(N1, net_kernel, connect_node, [N2]),
    true = rpc:call(N1, net_kernel, connect_node, [N3]),

    ok = rpc:call(N1, mnesia, start, []),
    ok = rpc:call(N2, mnesia, start, []),
    ok = rpc:call(N3, mnesia, start, []),

    {atomic,ok} = rpc:call(N1, mnesia, create_table, [store, [{attributes, record_info(fields, store)}]]),

    {ok, _Nodes} = rpc:call(N1, mnesia, change_config, [extra_db_nodes, nodes()]),
    [ 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]),

    stopped = rpc:call(N1, mnesia, stop, []),
    stopped = rpc:call(N2, mnesia, stop, []),
    stopped = rpc:call(N3, mnesia, stop, []),

    ok = slave:stop(N1),
    ok = slave:stop(N2),
    ok = slave:stop(N3),

    ok = net_kernel:stop(),
    ok.

spam.erl で保存して、試せます。

$ erl
Erlang/OTP 24 [erts-12.0.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Eshell V12.0.3  (abort with ^G)
1> c(spam).
{ok,spam}
2> spam:main().
=INFO REPORT==== 18-Jul-2021::11:27:18.802112 ===
    application: mnesia
    exited: stopped
    type: temporary

=INFO REPORT==== 18-Jul-2021::11:27:18.809240 ===
    application: mnesia
    exited: stopped
    type: temporary

=INFO REPORT==== 18-Jul-2021::11:27:18.817348 ===
    application: mnesia
    exited: stopped
    type: temporary

ok