9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Elixir ETSインメモリデータベースのバックアップと復元

Last updated at Posted at 2023-03-04

ElixirにはETSという便利なインメモリデータベースが備えついています。ETSのデータをバックアップする方法を調べたのでメモします。

ETSとDETS

  • ETS(Erlang Term Storage)
    • Erlang備え付けのメモリー内のデータ保管庫
  • DETS(Disk-based ETS)
    • Erlang備え付けのファイルに保存するデータ保管庫

依存関係

ETSもDETSもErlangの備え付けなので、Erlang/Elixir以外何もいりません。

テーブル名をきめる

インメモリーのETSとファイル保存のDETSの両方に対して同じ名前を使います。別々の名前にしても良いのですが、共通の名前にすることにより後にコードが簡素化できる利点があります。

table_name = :my_data

DETSファイル保存先をきめる

ドキュメントには書かれてませんが、相対パスじゃないとダメなようです。絶対パスだとDETSテーブル生成時に変なエラーがでました。あと、パスがディレクトリ配下の場合はディレクトリが存在していないとエラーになります。

dets_dir = "tmp"
File.mkdir(dets_dir)

dets_path = Path.join([dets_dir, "#{table_name}.dets"])

ETSテーブルをつくる

テーブル名と各種オプションを指定してETSテーブルを作ります。

  • 名前をつけるか
    • named_table
  • データ構造
    • set
    • ordered_set
    • bag
    • duplicate_bag
  • アクセス権限
    • public
    • protected
    • private
  • 並行性
    • read_concurrency
    • write_concurrency

named_tableオプションにより名前付きテーブルにするとテーブルへの参照が楽になります。本記事のコードは名前付きテーブルを前提としています。

詳しくは原典をご参照ください。

init_my_ets = fn table_name ->
  :ets.new(table_name, [
    :named_table,
    :bag,
    :public
  ])
end

init_my_ets.(table_name)

DETSテーブルをつくる

ファイルは相対パスで、ディレクトリを含むパスの場合はディレクトリが存在している必要があります。

同期させるためには、DETSテーブルのデータ構造(ここでは:bag)をETSと合わせる必要があります。

初期設定では3分に一度ファイル保存されるようです。これは:auto_saveオプションにより変更可能です。

詳しくは原典をご参照ください。

:dets.open_file(table_name,
  type: :bag,
  file: dets_path |> String.to_charlist(),
)

DETSファイルに保存されたデータをETSに注入する

現時点ではDETSファイルは空だと思いますが、DETSにより保存されたデータが入っている場合は、:dets.to_ets/2を用いてETSに注入するすることができます。

ETSテーブルの中身は:ets.tab2list/1で確認できます。

:dets.to_ets(table_name, table_name)
:ets.tab2list(table_name)

ETSのデータを消してみる

後にデータを消したりしたくなると思います。ETSのデータを消す方法はいくつかあります。簡単な方法を挙げます。

キーで検索して消去

:ets.delete/2

:ets.delete(table_name, :toukon)
:ets.tab2list(table_name)

全データ消去

:ets.delete_all_objects/1

:ets.delete_all_objects(table_name)
[] = :ets.tab2list(table_name)

ETSにデータを挿入してみる

余談ですが、今回はたまたまデータ構造を:bagにしているのでキーの重複が認められます。キーを名前空間として利用できます。

entries = [
  {:toukon, %{x: 1}},
  {:toukon, %{x: 2}},
  {:autorace, %{x: 3}}
]

for entry <- entries do
  true = :ets.insert(table_name, entry)
end

:ets.tab2list(table_name)

ETSテーブルをDETSファイルに保存する

  • :dets.from_ets/2でETSテーブルの内容をDETSテーブルに保存します
  • :dets.sync/1でDETSテーブルに対して行われたすべての更新が確実にファイルに書き込まれるようにします
persist_data = fn table_name ->
  with :ok <- :dets.from_ets(table_name, table_name),
       :ok <- :dets.sync(table_name) do
    :ok
  else
    {:error, reason} ->
      Logger.error("Unable to sync DETS #{table_name}, #{inspect(reason)}")
  end
end

persist_data.(table_name)

ETSテーブルを消去してみる

:ets.delete/1

:ets.delete(table_name)

init_my_ets.(table_name)
[] = :ets.tab2list(table_name)

ETSを復元してみる

:dets.to_ets/2

:dets.to_ets(table_name, table_name)
:ets.tab2list(table_name)

:tada: :tada: :tada:

このように使い方を整理すれば、意外と簡単にできてしまいます。

ベテランの方々は簡素なパターンの場合には第三者パッケージに依存せずに自分で直にETSを使う人が多い印象をもっています。

参考文献

Elixirコミュニティ

本記事は以下のモクモク会での成果です。みなさんから刺激と元氣をいただき、ありがとうございました。

もしご興味のある方はお気軽にご参加ください。

生産者の皆様いつも美味しい食材をありがとうございます。おかげで健康に元氣にもくもく取り組むことができます。

9
1
0

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
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?