2009-12-19
ETSテーブルに対する排他的制御
erlang | |
Erlang付属のデータベースシステムにMnesiaというものがあって,これを使えば排他的制御とか普通に可能なんだけど,使用方法が複雑で頭の悪い自分が使うには難しい(というか面倒くさい).だから,特にテーブルなどが必要の無いシステムを実装するのであれば,極力ETSを使うようにしてる.
ここで問題になるのが,ETSには排他的制御が存在しないということ.複数のプロセスが動作しているシステムの中で共通のデータベースを利用することにおいて,これは結構致命的だしバグの原因になると思う.Mnesia使えば済むことなのだが,前述の通り面倒だ.そこで,自分でロック機構を実装してみた.
多分車輪の再発明で,他にも良い手法があるのだろうけど,軽量プロセスの意外な利用方法とかを垣間見ることができたので自分的には満足.
start() -> %% hogehoge ets:new('Lock-Daemon', [set, public, named_table]). lock(Table, Key, Func) -> Ref = make_ref(), case ets:lookup('Lock-Daemon', Key) of [] -> ets:insert('Lock-Daemon', {Key, spawn(fun lock_daemon/0)}), [{Key, Daemon}] = ets:lookup('Lock-Daemon', Key), Daemon ! {{self(), Ref}, {update, Table, {Key, Func}}}; [{Key, Daemon}] -> Daemon ! {{self(), Ref}, {update, Table, {Key, Func}}} end, receive {Ref, ok} -> ok end. lock_daemon() -> receive {{From, Ref}, {update, Table, {Key, Func}}} -> [Item] = ets:lookup(Table, Key), case Func(Item) of {ok, Result} -> ets:insert(Table, Result); {error, _Reason} -> pass end, From ! {Ref, ok} end, lock_daemon().
2009-12-06
gen_serverで例外が発生したら再起動させるメモ
Erlang/OTPに含まれるgen_serverビヘイビアにおいて,例外が発生した際にサーバを再起動させるよう実装しようとして時間を無駄にしてしまったのでメモしておく.
まずgen_serverビヘイビアの動作について軽く説明すると,もし例外が発生したらterminate/2コールバック関数が呼び出されることになっている.で,その関数内でサーバを再起動するわけだけど,少し注意が必要.なぜなら大抵の場合,再起動ということでstart_linkに同じ名前を指定するのだけれど,terminateが呼び出された時点ではまだサーバが死んでなくて二重登録となるからなのか,思ったとおりに動いてくれなかった.
あと,理由は分からないけれど,別プロセスに再起動してもらう必要があった.これは環境やタイミングによるのかもしれない.
terminate(Reason, State) -> io:format("terminate: ~p~n", [Reason]), spawn(fun() -> unregister(?MODULE), gen_server:start_link({local, ?MODULE}, ?MODULE, State, [{debug, [trace, log]}]), io:format("restart gen_server~n") end), ok.
