Node.jsでストリーム処理を行っているときに、以下のような警告が表示されることがあります。
(node:25604) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 finish listeners added to [Transform]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limit
この警告は、ストリームで使用している Transform
ストリームが finish
イベントのリスナーを10個以上追加したことが原因です。通常、Node.jsの EventEmitter
(Transform
はそのサブクラス)はリスナーが10個以上追加されると警告を出す仕様になっています。この警告が出る理由とその解決方法について説明します。
原因
MaxListenersExceededWarning
の警告が出る原因は、Transform
ストリームを使い回しているためです。Transform
ストリームは、内部でイベント(特に finish
イベント)を発火させますが、インスタンスを使い回すことでそのリスナーが複数回追加されてしまいます。これにより、10個以上のリスナーが追加され、警告が発生します。
具体的な例
以下のコードでは、Transform
ストリームのインスタンス basicTransform
を使い回しています。await pipeline()
の中で再利用すると、finish
イベントに対するリスナーが積み重なり、警告が発生します。
問題のコード
// データ変換のためのTransformストリームを定義
const basicTransform = new Transform({
// オブジェクトモードを有効にして、オブジェクトの読み書きを可能にする
objectMode: true,
// 各チャンクを変換するための関数
transform(chunk, _encoding, callback) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const data = evolve(chunk, {
status: changeApplicationStatus,
// ...
});
callback(null, data);
},
});
このコードでは basicTransform
を一度作成し、それを複数回使い回しています。これが原因で、finish
イベントにリスナーが追加されていきます。
解決方法
1. Transform
ストリームを毎回インスタンス化する
Transform
ストリームのインスタンスを使い回さず、毎回新しいインスタンスを作成することで、リスナーの積み重ねを防ぎます。この方法を採用すると、ストリームごとにリスナーが適切に管理され、警告が発生しません。
修正後のコード
// Transformストリームのインスタンスを毎回作成する関数
const basicTransformFactory = (): Transform => {
return new Transform({
// オブジェクトモードを有効にして、オブジェクトの読み書きを可能にする
objectMode: true,
// 各チャンクを変換するための関数
transform(chunk, _encoding, callback) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const data = evolve(chunk, {
status: changeApplicationStatus,
// ...
});
callback(null, data);
},
});
};
export { basicTransformFactory };
この修正により、basicTransformFactory
を呼び出すたびに新しい Transform
インスタンスが作成されるので、リスナーの重複がなくなります。
2. removeAllListeners
の使用
もし、どうしても同じ Transform
ストリームを使い回さなければならない場合は、ストリームを使用した後に removeAllListeners
を呼び出して、不要なリスナーを手動で削除することができます。ただし、これはあまり推奨されません。適切なインスタンス管理が重要です。
上手く削除できない場合があります。
basicTransform.removeAllListeners();
最後に
ストリームの使い回しによる MaxListenersExceededWarning
の警告を避けるためには、Transform
ストリームをインスタンス化する際に毎回新しいものを生成するようにしましょう。この方法により、リスナーの重複を防ぎ、ストリームの挙動を安定させることができます。