supervisorのメッセージキューが溜まる
つい先日, supervisorのメッセージキューが溜まり続けていたので教訓として残す.
start_child, terminate_child, etc...の実装について
start_childを例に取る.
見ての通り, supervisorはgen_server behaviourで定義されており, start_childは内部的にはgen_server:call/3
(timeout: infinity
) が呼ばれる.
その後, ここでChildSpec
に則り, 起動処理を行う.
起動処理は単純でerlang:apply/3
の結果を見て, 成功していればChildSpec
を保存・管理する.
gen_serverの起動処理
子の起動処理として以下の関数をここでは以下を想定しよう.
-module(hoge_server).
-behaviour(gen_server).
-compile(export_all).
start_link(Args) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
init(Args) ->
{ok, Args}.
%% 以下略
supervisorのdo_start_child
内のerlang:apply/3
で実行されるのは, hoge_server:start_link/1
である.
この中のgen_server:start_link
はinit/1
の処理が終わるまで結果を返さない.
(proc_lib:start_linkでsync_wait
で待っている)
supervisorのメッセージキューが溜まる条件
以上を踏まると,
Mod:start_link
+ Mod:init
にかかる時間に呼ばれるstart_child
の回数が1回を超えるとメッセージキューが溜まることが分かる.
あとは, 処理待ち時間が増えていくのでstart_child
が減らない限りは溜まり続けるだけだ.
ちなみに, gen_server
behvaiourのterminate
処理に時間がかかっても同様の事が実現できる.
つまり, start_link, init, terminate で通信を行うような重い処理をしてはならない.
パーツパーツとしては分かっていたつもりだったが...
- 帯域が埋まり, ラック内のサーバーへのRTTが40msになる.
- start_link, initで行っていた, 分散KVSへのリクエストに時間がかかる.
- supervisorのメッセージキューがたまる.
-
gen_server:call
->supervisor:start_child
してたのでtimeoutしまくる.
素晴らしいコンボだった.