mnesia_key_cache を公開しました

他の誰が嬉しいのかわかりませんが、自分が嬉しいライブラリです。

https://github.com/voluntas/mnesia_key_cache

mnesia にはランダムに一つキーを持ってくるという機能があることにはあるのですが、
その mnesia:first/1 はテーブルロックしてしまって大量アクセスするとデッドロックとかが生まれてしまいます。

どんなライブラリか

まずなにが嬉しいのかというと、指定したテーブルから適当にキーを一つ持ってきてくれます。
ほぼ mnesia:first/1 と一緒です。違うのはテーブルロックを行わないということです。

構造としては事前にテーブルのキーをキャッシュしておいてそれを配布するという仕組みです。

キャッシュがからになったら再度 mnesia を見に行きます。それでもからだったら {error, not_found} を返します。

使い方

github においています、ライセンスは Erlang Public License (ほぼ MPL です)

git clone git://github.com/voluntas/mnesia_key_cache.git

あとは make して頂ければ大丈夫です。rebar の deps でした方が使いやすいかも知れません。

mnesia:start/0 して、あとはキャッシュしたいテーブルを指定します。

mnesia_key_cache:start(ここにテーブル名)

mnesia:activity/2 をよく使うのでそれをラップした mnesia_key_cache:activity/2 を作りました。

サンプルコード

-module(sample).

-export([main/0]).

-define(STORE_TABLE, store_table).

-record(store, {key :: binary(),
                value0 :: binary(),
                value1 :: binary()}).

add_store(_) ->
  mnesia:activity(async_dirty,
                  fun mnesia:write/3,
                  [?STORE_TABLE,
                   #store{key    = crypto:rand_bytes(32),
                          value0 = crypto:rand_bytes(32),
                          value1 = crypto:rand_bytes(32)},
                   sticky_write],
                  mnesia_frag).

main() ->
  %% mnesia を起動
  mnesia:start(),
  %% mnesia_key_cache を起動
  application:start(mnesia_key_cache),
  %% テーブルを作る
  mnesia:create_table(?STORE_TABLE,
                      [{frag_properties,
                        [{node_pool, [node()]},
                         {n_fragments, 128}]},
                       {record_name, store},
                       {attributes, record_info(fields, store)}]),
  %% 500 件ほどデータを追加
  lists:foreach(fun add_store/1, lists:duplicate(500, dummy)),
  %% キャッシュ開始
  mnesia_key_cache:start(?STORE_TABLE),
  %% この時点ではキャッシュには 500 件しか入っていません
  %% さらに mnesia には 500 件追加
  %% キャッシュ 500、mnesia 1000 という状態です
  lists:foreach(fun add_store/1, lists:duplicate(500, dummy)),
  F = fun(Key) ->
        case mnesia:read(?STORE_TABLE, Key, write) of
          [] ->
            {error, retry};
          [_] ->
            mnesia:delete(?STORE_TABLE, Key, write),
            ok
        end
      end,
  %% とりあえず 500 件キーを受け取ってキャッシュをからにします
  lists:foreach(fun(_) ->
                  ok = mnesia_key_cache:activity(?STORE_TABLE, F)
                end,
                lists:duplicate(500, dummy)),
  %% このタイミングで再度キャッシュされます
  ok = mnesia_key_cache:activity(?STORE_TABLE, F)
  mnesia:stop(),
  application:stop(mnesia_key_cache).

今後

  • mnesia:all_keys の実態は mnesia:select らしいのでキャッシュの量を指定できるようにする。
  • mnesia:select の MatchSpec を指定できるようにしてキャッシュするキーの条件などを指定できるようにする。

あまり需要がない仕組みではありますが、なんかしらデータを一つ取りたいという人には重宝するはずです。