2011年04月18日

erlシェルからの起動と、-detached指定でデーモン起動したときの動作が違う(デタッチするとサービスが落ちる)

Simpledungeonをスーパバイザ挙動を使って動かす作業中です。

erlシェルからの起動と、-detached指定でデーモン起動したときの動作が違う(デタッチするとサービスが落ちる)という現象に出くわしました。

コマンドラインで
  erl -pa /opt/yaws/lib/yaws/ebin
と起動して、erlang shellから
  simpledungeon:start().
と入力すると、サービスはちゃんと動く。

でも、同じバイナリで
  erl -pa /opt/yaws/lib/yaws/ebin -noinput -noshell -run simpledungeon start -detached &
というふうに、デーモン起動すると、なぜかサービスが動いてないわけです。

いろいろ見て回って、最終的に檜山さんのまとめた"helloちゃんと作るシリーズ"の記述に答えがありました。

http://erlang.g.hatena.ne.jp/m-hiyama/20090114/1231891538

ここ。

要するに、デーモン起動したときだけサービスが動かないというのは、start_linkで起動してるのに親シェルがデタッチしたためにサービスが止まった、ということのようです。

で、檜山さんのスケルトンを参考に、unlinkの1行を追加したところ、Erlangシェルでスーパバイザを起動したときと同じように、サービスが動き続けてくれました。

=======================
修正前:
  {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE,[]),
=======================
修正後:
  {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE,[]),
  unlink(Pid),
=======================

うー、つかれた。

start_linkしてunlinkするんだったら、素直に「start」させてくれよ、という気もしますが、そこはOTPの長年の熟成の中で入ってないわけですから、ひとまずこの作法に従うことにします。まあ、unlinkがいるのは、シェルから起動するアプリケーション最上位のsupervisorだけ、それ以外は全部linkしててほしいので、この構成は正しいと考えるのが妥当でしょうね。
posted by へろ at 01:39| Comment(0) | TrackBack(0) | Erlang

supervisorでハマッた

分かってしまえば、ものすごくシンプルなんだけど、simpledungeon開発中にハマッた件。

Erlang/OTPの、supervisor ビヘイビアで、start_linkに渡すスーパバイザ名と、start_childに渡すスーパバイザ名の書き方に違いがあるということに気づかず、謎のエラーが出続けて1週間ほどタイムロスしてました。

●スーパバイザの起動

supervisor:start_link({local,?MODULE},?MODULE,[])

のように書いたわけです。この記述には問題はありません。

●子プロセスであるyawsを起動するところ

子プロセスの起動側に問題がありました。

誤:[supervisor:start_child({local,?MODULE}, Ch) || Ch <- ChildSpecs]
正:[supervisor:start_child(?MODULE, Ch) || Ch <- ChildSpecs]

ずっと、誤の書き方をしていて、下のようなエラーメッセージが出続けていました。なんでnodedownやねん、と。このエラーメッセージの原因が分からなかったわけです(相対ディレクトリ指定ができないのかなとか、どっかがリストになってないのかな、とか)。でも、全然原因は違ってました。。。

=エラーメッセージ:=============
1> simpledungeon:start().
** exception exit: {{nodedown,simpledungeon},
{gen_server,call,
[{local,simpledungeon},
{start_child,
{yaws_log,
{yaws_log,start_link,[]},
permanent,5000,worker,
[yaws_log]}},
infinity]}}
in function gen_server:call/3
in call from simpledungeon:'-start_yaws/0-lc$^0/1-0-'/1
in call from simpledungeon:start_yaws/0
in call from simpledungeon:start/0
2>
===================

原因はシンプルで、start_childの第1引数には、start_linkのときに指定していた{local, 名称}タプルではなく、名前のアトムを渡さないといけなかったのですね。

http://www.erlang.org/doc/man/supervisor.html

にある、supervisor:start_child/2の定義。
=======================
start_child(SupRef, ChildSpec) -> Result

Types:
SupRef = Name | {Name,Node} | {global,Name} | pid()
Name = Node = atom()
ChildSpec = child_spec() | [term()]
Result = {ok,Child} | {ok,Child,Info} | {error,Error}
Child = pid() | undefined
Info = term()
Error = already_present | {already_started,Child} | term()
=======================

SupRefが、「どのスーパバイザに子プロセスを起動させるか」の参照なわけですが、{global,Name}がある一方で、{local,Name}はなく、そのかわりにNameがあるわけです。

というわけで、スーパバイザとスーパバイズ対象の子プロセスをどちらもローカルで立ち上げるのであれば、start_linkでは{local, Name}を指定し、start_childではNameを渡すという起動方法になる、、、のですよ。

はまった。
posted by へろ at 01:20| Comment(3) | TrackBack(0) | Erlang