Connectionはちゃんと閉じよう(いましめ)
経緯
別のサーバーにアクセスして情報をとってくるnodejsが EMFILE, too many open files
を発してプロセス死亡多発。
OSはWindowsで特にファイルも開いてないし上限ないはずなんだが...と困惑する俺氏。
なにやらハンドルが増えすぎてエラーが出ているっぽいことに気がついたあたりで
コネクション張りすぎとサーバー管理者から怒られて事態を把握。
標準的なデータ取得例(Callback)
例えばMySQLから情報を取得するには下記のようにクエリーを投げて列で取得することがある。
クエリーの前に connect
してその後に end
する。ワカル。
connection.connect(); // <--
connection.query('SELECT * from database',(err, row)=>{
if (err) throw err;
row.map() // 列の処理
connection.end(); // <--
})
データ取得例(stream)
ところがstreamになるとどこで閉じるべきかという話になる。
コールバックがない場合、コネクションを閉じ忘れて、対象のコネクションが残ったままになる。
// データ処理するStream
const cst = Transform({objectMode:true
,transform:function(chunk, enc, cb){
// 処理
cb();
})
// クエリー結果と接続
const source_stream = queryStream("SELECT * from database");
source_stream
.pipe(cst);
// コネクション閉じてない
function queryStream(query){
connection.connect(); // <--
return connection.query('SELECT * from database').stream() // streamをかえす
}
callbackで渡される場合も同様
queryStreamCallback("SELECT * from database", (err, stream)=>{
stream
.pipe(cst);
// ここでendすると転送完了前に閉じてしまう
});
function queryStreamCallback(query, cb){
connection.connect(); // <--
connection.query('SELECT * from database',(err, stream)=>{
return cb(err, stream)
})
}
解決方法
直接Connectionにアクセスできる上記の例なら on('finish')
で開放すればいいのですが、
使い勝手を考えると隠蔽していることが多いので直接アクセスできない。
なのでStreamを渡す側が on("end")
に connection.end()
を登録までして返すほうが安心。
const source_stream = queryStream("SELECT * from database");
source_stream
.pipe(cst);
// 使用側はケアしなくて良い
function queryStream(query){
connection.connect(); // <--
let st = connection.query('SELECT * from database').stream() // streamをかえす
st.on("end", ()=>{ connection.end(); }) // <-- streamが閉じたらコネクションも閉じる
return st;
}
connect
しといて後は勝手に開放してくれるだなんて思ったらだめだゾ。
発見用メモ
もしかして開放忘れてる? と言うときに確認する方法
タスクマネージャのプロセス一覧にハンドル数が表示される(ない場合は列の編集でチェックを入れる)