Erlangにはdetsというモジュールがある。
ディスクを使うetsという位置付け。
単なる 埋め込みKVS だと思っていたけど :type オプションを使えば追記可能なリストになったりと便利だった。
利用の流れと Enum, Stream の練習としてテーブル全体のイテレーションを書いておく。
注意点
Detsの注意点を並べておく。ディスクを使うKVSだが特に因果順序が保たれたりしない。
- 同一プロセス内で共有される
- 同一パスに対するテーブルは並行制御されない
- syncするまでファイルに書き込まれているとは限らない
- openの仕方によってリファレンスかパスか異なる
- select,matchが線形探索
- テーブル全体のサイズが2GBまで
- erlangの文字列なのでElixirとしてはcharlistを使う
基本的な利用の流れ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
:dets.open_file('year:2020', type: :bag, file: 'tmp/smaple.2020.dets')
:dets.all() |> Enum.map(& :dets.info(&1))
for num <- 0..5 do
:dets.insert('year:2020', {num, "value #{num}"})
:dets.insert('year:2020', {num, "value #{num + Enum.random(1..20)}"})
end
0..5 |> Enum.map(& :dets.lookup('year:2020', &1))
:dets.sync('year:2020')
:dets.stop()
:dets.all()
:dets.open_file('year:2020', type: :bag, file: 'tmp/smaple.2020.dets')
0..5 |> Enum.map(& :dets.lookup('year:2020', &1))
:dets.close('year:2020')
:dets.all()
|
:dets.open_file/n
がDetsファイルを作成する。この際に file: キーワードでパスを指定しない場合、最初のデータベース名がファイルパスになる。
ファイルパスにファイルが存在しなければ自動的に作成されるが、すでに存在する場合にフォーマットが違うとエラーとなる。
全てのidを返す Streamの実装
こんな感じで書く。
1
2
3
4
5
6
|
defmodule Example do
def id_stream(name) do
Stream.iterate(:dets.first(name), & :dets.next(name, &1))
|> Stream.take_while(& &1 != :"$end_of_table")
end
end
|
使い方。
1
2
3
4
5
6
7
8
9
10
11
|
:dets.insert(:foo, {"hoge", "apple"})
:ok
:dets.insert(:foo, {"hoge", "orange"})
:ok
:dets.insert(:foo, {"foo", 100})
:ok
Example.id_stream(:foo) |> Enum.map(& &1)
["hoge", "foo"]
Example.id_stream(:foo) |> Enum.map(& :dets.lookup(:foo, &1))
[[{"hoge", "apple"}, {"hoge", "orange"}], [{"foo", 100}]]
|