続:gen_server のコールバックモジュール内で badarith が発生すると supervisor ごと落とされる

二年半前に gen_server のコールバックモジュール内で badarith が発生すると supervisor ごと落とされる というメモを残したにも関わらず、すっかり内容を忘れてしまい、変な Process Design の poolboy*1 の poolboy_sup が安全か検証をするのに時間が掛かったのでメモを残す。

もともと、Erlang ML で下記のようなやり取りがあった。
erlang-questions: Supervisor does not restart Gen Server
ざっくり要約すると Erlang Shell が停止してしまい、道連れで Supervisor が停止している。回避策は try catch …という内容。
これをすっかり忘れており「badarith 例外発生 = 全 Supervisor 停止」と勘違いして覚えていたのが、そもそもの勘違いの発端。
実際は、badarith が発生すると、"何故か" Erlang Shell が停止してしまい、Erlang Shell と link 関係にある Supervisor が "何故か" 停止している…が正しい。

下記に試した内容を列挙する。

badarith を発生させるプロセスの起動方法 結果
Erlang Shell -> Supervisor -> Worker(gen_server) Supervisor が Worker を再起動した後、erlang:apply/2 が停止し、link 先の Supervisor が道連れに停止
Erlang Shell -> Application -> Supervisor -> Worker(gen_server) Worker 再起動
Erlang Shell (spawn) -> システムプロセス(trap_exit = true) -> worker(gen_server) システムプロセスが Worker 停止のメッセージ受信
Erlang Shell (spawn_link) -> システムプロセス(trap_exit = true) -> worker(gen_server) システムプロセスが Worker 停止と erlang:apply/2 停止のメッセージ受信

Supervisor には、start は無く start_link しかない。Erlang Shell 上から start_link を実行すると、起動した Supervisor は Erlang Shell と link 関係になってしまう。この辺りが、関係しているようにも思えるが…下記の疑問が残る。

  • worker(gen_server) と直接 link 関係には無い Erlang Shell が、何故、worker(gen_server) の badarith を補足しているのか?
  • Supervisor ではないシステムプロセスは、Erlang Shell の EXIT メッセージを補足するのに、Supervisor の EXIT を補足する handle_info/2 は、何故、評価されていないのか?(print debug で確認済み)

疑問は残るが、とりあえず Erlang Shell が停止しなければ、Worker で badarith が発生しても平気であるため、下記の回避策が考えられる(おすすめ順)。

  • Application 化する(一番、おすすめ)
  • ML 推奨の try catch(throw や exit を補足するならともかく、error を補足するのは、ちょっと…)
  • Erlang Shell 上で catch_exception(true). を評価しておく(これの副作用は、現在、調査中)

既に Erlang 歴 3 年目なのに、こんな所で躓いているから、いつまでも Erlang 界隈の底辺なんだろうなぁ…

*1:後日、poolboy の紹介を書く予定