Part31:ETS

海未「最近になってアイマスを見始めたのですが」
希「・・・まあ、アイドルの研究は大事ってことにしとこうか」
海未「如月千早を見ていると、心が安まる気がします。特に水着回」
凛「海未ちゃん、自分より下を見つけて安心するのはよくないにゃ!」


海未「閑話休題。今回はETSの話でした」
凛「ETS?」
海未「Erlang Term Storageですね。メモリ上にデータを保存しておくための仕組みです。たとえば、これまでに作ってきたレジストリがありますね」
凛「あるにゃ」
海未「1つのレジストリで管理するbucketの数が増えるほど、レジストリに処理が集中してボトルネックになることが考えられます」
希「たしかに、bucket使いたいプロセスはみんなlookupするしなあ」
海未「そこで、bucketとレジストリの間にキャッシュとして動作するETSテーブルを挟むと効率化が期待できます」

海未「まず、簡単なETSの使い方を見てみましょう」

海未「:ets.newでETSテーブルを作ります。最初の引数で適当に名前を付けて、次にテーブルの動作を指定するオプションを渡しています」
凛「:setとか:protectedだよね。どういう意味なの?」
海未「テーブルにはキーバリューペアで値が格納されますが、:setはそのキーの重複を許さないということです。:protectedは、そのテーブルに書き込みができるのをテーブルを作成したプロセスに限定するものです」

海未「あとは、insertlookupで値の書き込みと読み出しができます」
希「これはそんなに難しくないね」

海未「では、いつものアプリケーションに組み込んでみましょう。KV.Registryから見ていきますね」

海未「だいぶ色々と変わっています。まずClient APIの部分ですが、起動時にETSテーブル名を受け取ってstateに入れて持ち歩くようにしています」
凛「start_linkの引数が増えてるんだよね」
海未「はい。それからlookup関数は、サーバと通信せずETSテーブルの中からbucketを探すようにしています」

海未「createからcastではなくcallを呼ぶようにしたのは、レースコンディションの問題を避けるためです」
凛「れーすこんでぃしょん?」
海未「castは非同期ですから、その先のhandle_castでbucketを作る処理が終わらないうちに先に進んでETSテーブルから取得しようとしてしまうことが起こり得るわけです」
希「あー、それだと他のプロセスとの競合みたいなのもありそうやね」
海未「ですから、ここはcallに変更して同期的に処理するようにしています」

海未「Server Callbackの方は、initで今までHashDictで管理していたところをETSに変更しています」
希「:read_concurrency?」
海未「多数のプロセスから同時にアクセスされる状況に対して最適化してくれるオプションですね」

海未「handle_callは、もともとhandle_castだったものを書き換えたものです。以前のhandle_callは削除しています」
凛「引数と戻り値が1つ増えてるんだね」
海未「はい。それと、bucketを:ets.insertでETSテーブルで管理するようになっています」

海未「最後にhandle_infoですが、bucketのレジストリからの削除を:ets.deleteで行うようにしています。KV.Registryについては以上です」

海未「レジストリを作るKV.Supervisorのコードも少し直す必要があります」

海未「KV.Registryを作るworker関数の引数に、さきほどのstart_linkの定義と合わせてテーブル名を追加しています」

海未「クライアントコードは前回のままにしましょう。全く同じように動くはずです」

希「外から見ると分からんけど、これでETS使うようになってるわけやね」
海未「実際にレジストリがボトルネックになるほど大量のプロセスを動かしてプロファイリングすれば違いがわかるのでしょうけど・・・今回は、そこまではしません」

海未「今の状態では、ETSテーブルはレジストリのプロセスとリンクしています。つまり、レジストリのプロセスが死んだ時点でETSテーブルも破棄されます」
希「ははーん、その言い方やと、テーブルの永続化ができそうやね」
海未「そういう勘はさすがですね。そうです。ETSテーブルとレジストリのプロセスを切り離すと、レジストリが死んでもデータを持ち続けることができます」
凛「Supervisorが自動で再起動してくれるのと合わせると最強な気がするにゃ」

海未「ETSテーブルを作成する処理を、KV.Supervisorに移動します」

海未「workerにテーブル名ではなく、ETSテーブル自体を渡すようにしています。ここでのポイントはテーブル作成時に:publicを指定していることです」
希「えーと、:protectedやとテーブルに書き込めるのはそれを作成したプロセスだけ、と」
海未「:publicはそれに対して、どのプロセスからでも書き込みができるというオプションです」
凛「あっ、:protectedだとKV.Registryから書き込めなくなっちゃうにゃ!」
海未「はい。テーブルを作っているのはKV.Supervisorになりましたから」

海未「KV.Registryの方は、テーブルを作らずに渡されたテーブルを使うようにします」

海未「これで、レジストリが再起動されてもデータが引き継がれるようになりました」

海未「これでアプリケーションはひとまず完成したのですが・・・」
凛「次は他のアプリケーションと通信できるようにしていくにゃ!」


LINEで送る
Pocket


返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です