LoginSignup
0

More than 3 years have passed since last update.

Elixirプログラムにmnesia_eleveldbモジュールを組み込もうとしてうまく動作しなかった問題

Last updated at Posted at 2019-06-14

はじめに

Erlangのライブラリには、ディスクに保存する型のKey-Valueストアとして detsが組み込まれている。ただし、detsには、ファイルサイズの2GB制限やordered_setをサポートしていないという制約がある。

Key-Valueストアのエンジンとして、Googleがオープンソースとして公開しているLevelDBは、よい代替手段である。eleveldbモジュールは、LevelDBをErlangに組み込んだものであり、mnesia_eleveldbモジュールは、Mnesiaの組み込みDBとしてeleveldbを利用するためのモジュールである。しかし、通常の手順でmnesia_eleveldbモジュールを組み込もうとして、うまくビルドできなかったため、解決までの手順のメモとしてこの記事を残す。

問題

Elixirのプロジェクトで、Mixの依存モジュールとして、mnesia_eleveldbを登録すると、eleveldbも依存モジュールとして組み込まれる。そのままビルドして実行すると、:mnesia.create_table/2 で leveldb_copiesオプションを指定して LevelDBのテーブルを作成しようとすると、

10:26:03.028 [warn]  The on_load function for module eleveldb returned:
{:error, {:load_failed, 'Failed to load NIF library: \'Cannot open "/xxx/src/elixir/test_mnesia_eleveldb/_build/dev/lib/eleveldb/priv/eleveldb.so"\''}}

というエラーが発生する。メッセージに記述されているとおり、eleveldb.soは作成されていない。mnesia_eleveldbモジュールを組み込まず、eleveldbモジュール単体を組み込んでビルドした場合は、eleveldb.soは作成された。

解決

Elixirのフォーラムで、eleveldbは、rebar3用に設定されていないからoverridesを使え( https://github.com/erlang/rebar3/issues/801 )、という記述があったので、記事の通りに修正してみる。

_build/dev/lib/eleveldb/mix.rebar.config を編集して、最終行のoverridesを追加する(パスの斜体部分は、/dev/や/rel/などになる)。

元の記述(部分)
{overrides,[]}.
修正後
{overrides,[
  {override, eleveldb,
     [
       {artifacts, ["priv/eleveldb.so"]},
       {pre_hooks, [{compile, "c_src/build_deps.sh get-deps"},
                    {compile, "c_src/build_deps.sh"}]},
       {post_hooks, [{clean, "c_src/build_deps.sh clean"}]},
       {plugins, [pc]},
       {provider_hooks, [{post,
                           [{compile, {pc, compile}},
                            {clean, {pc, clean}}
                           ]
                        }]
        }
     ]
  }
]}.

その後、make し直す。

% cd deps/eleveldb
% make
./rebar get-deps
==> eleveldb (get-deps)
./rebar compile
==> eleveldb (compile)
gmake[1]: ディレクトリ '/xxx/src/elixir/test_mnesia_eleveldb/deps/eleveldb/c_src/leveldb' に入ります
gmake[1]: 'all' に対して行うべき事はありません.
gmake[1]: ディレクトリ '/xxx/src/elixir/test_mnesia_eleveldb/deps/eleveldb/c_src/leveldb' から出ます
gmake[1]: ディレクトリ '/xxx/src/elixir/test_mnesia_eleveldb/deps/eleveldb/c_src/leveldb' に入ります
gmake[1]: 'tools' に対して行うべき事はありません.
gmake[1]: ディレクトリ '/xxx/src/elixir/test_mnesia_eleveldb/deps/eleveldb/c_src/leveldb' から出ます
Compiling c_src/eleveldb.cc
Compiling c_src/refobjects.cc
Compiling c_src/router.cc
Compiling c_src/workitems.cc
% ls priv/
eleveldb_multi.schema  eleveldb.so*           perf_dump*             sst_scan*
eleveldb.schema        leveldb_repair*        sst_rewrite*           
%

eleveldb.soが作成されていることを確認した。

テスト

% iex -S mix
Erlang/OTP 21 [erts-10.3.5.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :mnesia.create_schema([node()])
:ok
iex(2)> :mnesia.start
:ok
iex(3)> :mnesia_eleveldb.register
{:ok, :leveldb_copies}
iex(4)> :mnesia.info
---> Processes holding locks <--- 
---> Processes waiting for locks <--- 
---> Participant transactions <--- 
---> Coordinator transactions <---
---> Uncertain transactions <--- 
---> Active tables <--- 
schema         : with 1        records occupying 428      words of mem
===> System info in version "4.15.6", debug level = none <===
opt_disc. Directory "/xxx/src/elixir/test_mnesia_eleveldb/Mnesia.nonode@nohost" is used.
use fallback at restart = false
running db nodes   = [nonode@nohost]
stopped db nodes   = [] 
master node tables = []
backend types      = leveldb_copies - mnesia_eleveldb
remote             = []
ram_copies         = []
disc_copies        = [schema]
disc_only_copies   = []
[{nonode@nohost,disc_copies}] = [schema]
3 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
iex(5)> :mnesia.create_table(:Constant, [leveldb_copies: [node()]])
{:atomic, :ok}
iex(6)> :mnesia.info
---> Processes holding locks <--- 
---> Processes waiting for locks <--- 
---> Participant transactions <--- 
---> Coordinator transactions <---
---> Uncertain transactions <--- 
---> Active tables <--- 
Constant       : with 0        records occupying 292      words of mem
schema         : with 2        records occupying 552      words of mem
===> System info in version "4.15.6", debug level = none <===
opt_disc. Directory "/xxx/src/elixir/test_mnesia_eleveldb/Mnesia.nonode@nohost" is used.
use fallback at restart = false
running db nodes   = [nonode@nohost]
stopped db nodes   = [] 
master node tables = []
backend types      = leveldb_copies - mnesia_eleveldb
remote             = []
ram_copies         = []
disc_copies        = [schema]
disc_only_copies   = []
leveldb_copies     = ['Constant']
[{nonode@nohost,disc_copies}] = [schema]
[{nonode@nohost,leveldb_copies}] = ['Constant']
4 transactions committed, 0 aborted, 0 restarted, 4 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
:ok
iex(7)> Enum.each(1..100, fn e -> :mnesia.dirty_write({:Constant, e, String.duplicate("abc", e)}) end)
:ok
iex(8)> :mnesia.dirty_read({:Constant, 3})
[{:Constant, 3, "abcabcabc"}]
iex(9)> 

蛇足

じゃあ何で eleveldbモジュール単体だとビルドが通るのか? Mixやrebarの仕組みが、まだよくわかっていません。あしからず。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0