はじめに
Node.jsのアプリケーションサーバーがメモリリークしているようで、数日おきにクラッシュしていたので原因を調べました。
ひと昔前はクラッシュしたらサービス停止して大変だったりしますが、最近はセルフヒーリングで立ち上がってくるのでクリティカルにならないのは進歩を感じます(老人会)
3点ヒープダンプ法も試しましたが、原因特定に至らなかったので地道に仮説検証を繰り返しました。
使用メモリ量を出力する
以下のコードでファイルに出力して、Excelに貼り付けてグラフにすれば十分です。
起動時には--expose-gc
オプションをつけます。
import * as fs from 'fs';
function logHeapUsage() {
try {
global.gc();
} catch (e) {
console.log("You must run program with '--expose-gc'");
process.exit();
}
const heapUsed = process.memoryUsage().heapUsed;
console.log('Program is using ' + heapUsed + ' bytes of Heap.');
fs.appendFile('heapUsed.log', heapUsed + '\n', (error) => {
if (error) {
console.log(error);
}
});
}
setInterval(logHeapUsage, 2000);
Gatlingを使って大量のリクエストを送る
以下のスクリプトで、ローカル環境でエラーが出ない程度に調整してリクエストを送ります。
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class MemoryLeak extends Simulation {
val httpProtocol = http.baseUrl("http://localhost:3030")
val memoryLeak = scenario("MemoryLeak")
.repeat(100){exec(http("TopPage").get("/"))}
setUp(
memoryLeak.inject(
rampUsers(200) during (100 seconds),
nothingFor(30 seconds),
rampUsers(200) during (100 seconds),
nothingFor(30 seconds),
rampUsers(200) during (100 seconds),
).protocols(httpProtocol)
)
}
仮説・検証サイクルを回す
問題を再現できたら半分解決したようなものなので、あとは問題箇所を絞り込みながら検証を繰り返します。
まずはHello Worldのようなシンプルなエンドポイトを用意すると、アプリケーションコードに問題があるのか、フレームワークに問題があるのか切り分けできて良いかと思います。
今回の原因
Node.jsのv12.13.0が原因だったので、v14.17.0にバージョンアップして解決しました。
参考URL: https://bleepcoder.com/ja/node/613342361/node-v12-16-2-memory-issues-after-upgrade-from-v8-last-few
以上です。