代表的な面接トピック

Node.js 面接対策:samplePerIteration を使用してイベントループ遅延を診断するには?

コーディング難しい
Offer.cc 編集チーム公開日 更新日

質問

間欠的なテールレイテンシのスパイクが発生している Node.js API を担当しています。monitorEventLoopDelay を使用して、イベントループのブロックと依存関係の遅延を切り分け、タイマーベースのサンプリングと samplePerIteration、アイドル時の挙動、オーバーヘッド、検証方法を比較してください。

プロンプトと適用範囲

ある Node.js API において、トラフィックピーク時に P99 レイテンシのスパイクが発生していますが、データベースや外部サービスのレイテンシは同時に上昇していません。ブロックされたイベントループと遅延している依存関係を切り分ける診断を設計してください。ランタイムは Node.js 26.5.0 であり、その monitorEventLoopDelay API には、インターバルベースのサンプリングを維持しつつ、イベントループのイテレーションごとに 1 回サンプリングする samplePerIteration が追加されています。

サンプリングのセマンティクス、ナノ秒単位、ヒストグラムのライフタイム、アイドルプロセスの挙動、そして観測メトリクスをリクエストレイテンシと混同してはならない理由を説明してください。

面接官が評価するポイント

面接官は、モードを選択する前に、ループ遅延が何を測定しているのかについての正確な定義を求めています。優れた回答では、ヒストグラムを有効化、読み取り、無効化する必要があり、そのウィンドウをリクエストメトリクスと整合させる必要があることを説明します。また、サンプリングモードを変更するとサンプル分布が変わるため、異なるモード間の P99 値は直接比較できないことを認識しているかも確認します。

最も優れた回答は、ループ遅延を CPU、ガベージコレクション(GC)、キューイング、ダウンストリームのレイテンシ、インスタンス負荷と関連付けます。低コストな定常状態の監視、短時間の高解像度診断ウィンドウ、そしてロールバックと対照比較を提案します。

回答前の確認事項

  • ローカルのブロッキングを特定するのか、それともエンドツーエンドの P99 を直接説明するのか?
  • これは長時間実行されるプロセスか、サーバーレスインスタンスか、それとも短命な CLI か?
  • 許容される診断ウィンドウ、サンプリング解像度、監視オーバーヘッドはどの程度か?
  • すべてのインスタンスが Node.js 26.5.0 なのか、それともバージョンが混在しているのか?
  • CPU、GC、リクエストキュー、依存関係レイテンシ、イベントループ利用率(ELU)のメトリクスも取得可能か?

30秒で答える回答フレームワーク

「イベントループ遅延は、リクエストレイテンシではなく、ループの進行が期待より遅れて観測された時間として定義します。Node.js はヒストグラム値をナノ秒で報告します。定常状態の監視にはインターバルサンプリングを使用し、イテレーションごとのサンプリングは短時間の診断ウィンドウでのみ評価します。各モードはサンプル生成メカニズムが異なるため、パーセンタイル値には別々のベースラインが必要です。固定ウィンドウでヒストグラムを開始・停止し、P50、P99、最大値、サンプル数を記録して、CPU、GC、依存関係レイテンシ、リクエスト P99 と相関を取ります。ループ遅延のみが上昇している場合は、同期的な CPU 処理、システムコール、スタックトレースを調査します。」

ステップごとの詳細な回答

メトリクスの境界を定義する

monitorEventLoopDelay はナノ秒単位の遅延ヒストグラムを返します。これはサンプリングポイントで観測されたイベントループの進行遅延を表すものであり、リクエストの完全なネットワークライフサイクルをカバーするものではなく、単独でブロッカーを特定することはできません。ユーザーから見える P99 を説明するには、同じウィンドウ内のリクエスト開始、キューイング、アプリケーション処理、ダウンストリーム時間と整合させる必要があります。

サンプリングモードの選択

デフォルトモードは resolution で制御されるタイマーでサンプリングを行い、低コストな定常状態の監視に適しています。Node.js 26.5.0 では、ループのイテレーションごとに 1 回サンプリングする samplePerIteration: true が追加されました。ドキュメントには、このモードがプロセスのアイドル中に追加のイテレーションを強制したり、ループを実行し続けさせたりすることはないとも記載されています。

イテレーションごとのサンプリングは、インスタンスで継続的なループアクティビティがある場合の短時間のブロッキング事象の診断に役立ちます。ただし、サンプルの数や分布が異なるため、一方のモードの P99 をもう一方のモードの P99 に対するリグレッション判定の結果として使用することはできません。

ヒストグラムのライフタイム管理

ヒストグラムは永続的なグローバルアキュムレータとしてではなく、ウィンドウの状態として扱います。診断ウィンドウの開始時に作成して enable() し、終了時に percentile(99)maxcount を読み取り、その後 disable() してスナップショットをエクスポートまたは破棄します。メトリクス名には、サンプリングモード、解像度、Node バージョン、ウィンドウ境界を含める必要があります。

js
import { monitorEventLoopDelay } from 'node:perf_hooks';

const histogram = monitorEventLoopDelay({
  resolution: 20,
  samplePerIteration: true,
});

histogram.enable();
setTimeout(() => {
  const snapshot = {
    p99Ns: histogram.percentile(99),
    maxNs: histogram.max,
    samples: histogram.count,
  };
  histogram.disable();
  console.log(snapshot);
}, 10_000);

境界での単位変換

ヒストグラムの値はナノ秒です。正確な比較のために生値を保持しつつ、ミリ秒にするには 1_000_000 で割ります。最大値を SLA として扱わないでください。単一の外れ値は、一時停止、プロセスのフリーズ、または測定境界に起因する可能性があります。固定ウィンドウにおける P99、P999、最大値、サンプル数、および時系列データを優先して使用します。

考えられるブロッカーとの相関

ループ遅延と CPU が同時に上昇している場合は、同期的な JSON シリアライズ、カタストロフィックな正規表現処理、圧縮、暗号化、巨大な配列の走査などを検査します。GC がそれに伴って上昇している場合は、ヒープの増加とアロケーションレートを検査します。CPU が低いにもかかわらずループ遅延が高い場合は、同期システムコール、ロック待ち、またはホストのスケジューリングを調査します。依存関係レイテンシとリクエスト P99 のみが上昇している場合、ローカルループのメトリクスは依存関係のトレーシングの代わりにはなりません。

バージョン混在とオーバーヘッドの処理

起動時にランタイムバージョンを記録し、バージョンごとにメトリクスを分割します。26.5.0 より古いインスタンスには samplePerIteration が存在しないため、暗黙的にそのオプションが利用可能であると仮定しないでください。定常監視には大きめの resolution を使用し、インシデント時の短時間のイテレーションごとのウィンドウには機能フラグ(feature flag)を使用します。継続的な高頻度サンプリングは観測コストを増加させ、バージョン間の比較を困難にします。

検証とロールバック

同期 CPU ブロッキング、タイマーブロッキング、GC プレッシャー、およびアイドル状態のプロセスを用いて、コントロールされたサンプルを作成します。単位変換、クリーンな有効化/無効化の動作、アイドル時に人為的な起動が発生しないこと、障害時のリクエスト P99 との整合性を検証します。サンプリングコストやノイズがサービスに影響を与える場合は、診断フラグを無効にしてインターバルサンプリングに戻します。その際、バージョンとモードがラベル付けされた対照サンプルを保持します。

質の高い模範解答

monitorEventLoopDelay はリクエストレイテンシではなく、ローカルのスケジューリング健全性シグナルとして扱います。低コストな定常状態のサンプリングには resolution を使用し、Node.js 26.5.0 のイテレーションごとのサンプリングは、短時間のブロッキングを対象とした短い診断ウィンドウでのみ有効にします。各ウィンドウごとに 1 つのヒストグラムを作成、有効化、読み取り、無効化し、ナノ秒は一貫してミリ秒に変換します。P99 を CPU、GC、依存関係レイテンシ、リクエスト P99 と整合させます。ローカルで同時に上昇が見られた場合にのみ、同期 CPU、システムコール、またはスケジューリングへと調査を進めます。2 つのモードには別々のベースラインが必要であり、混在するランタイムバージョンは分割して管理する必要があります。」

よくある間違い

  • ループ遅延をリクエストレイテンシとして扱う → ダウンストリームやキューイングの時間が不可視のままになる → レイヤーを定義し、リクエストトレーシングと整合させる。
  • ナノ秒であることを忘れる → レポートの値が 100 万倍ずれる → 境界で変換し、生値も保持する。
  • 異なるモード間で P99 を比較する → サンプル生成メカニズムが異なる → モード固有のベースラインを構築する。
  • イテレーションごとのサンプリングを常時有効にする → コストとノイズが残り続ける → 通常時は低コストでサンプリングし、インシデント時に短時間強化する。
  • アイドル時の挙動を無視する → サンプリングがインスタンスを起動させているかどうかを誤認する → アイドル状態のプロセスで明示的にテストする。
  • ランタイムバージョンを集約してしまう → 古いインスタンスにはオプションが存在しない → バージョンごとにタグ付けして集約する。

フォローアップの質問と回答

フォローアップ 1:イテレーションごとの P99 が高いことはパフォーマンスの劣化を証明しますか?

いいえ。観測ポイント、サンプル数、および分布が変化したためです。同じ負荷、バージョン、ウィンドウの下で両方のモードを実行し、それぞれを独自のベースラインと比較した上で、監視オーバーヘッドを測定する際には CPU、スループット、リクエスト P99 を比較します。

フォローアップ 2:なぜ CPU が低いのにループ遅延が高くなることがあるのですか?

同期システムコール、ロック待ち、ホストのスケジューリング、またはプロセスの一時停止により、ユーザー空間の CPU 使用率が高くなくても進行が遅れることがあります。ブロッキングが存在しないと決めつけるのではなく、ランタイム診断、システムメトリクス、スタックトレースを組み合わせて調査します。

フォローアップ 3:サーバーレス関数でこのヒストグラムを永続的に有効にしておくべきですか?

通常、リクエストをまたぐ主要なシグナルとしては適していません。インスタンスのライフタイムが短く、ウィンドウが途切れる可能性があるためです。診断ビルドでは、コールドスタート、実行時間、ダウンストリームトレースを記録しながら、呼び出しごと、または短いバッチごとにサンプリングすることができます。

フォローアップ 4:サンプリングがアイドル時の挙動を変更しないことをどのように証明しますか?

各モードにおいて、タイマーやリクエストのないプロセスを実行します。終了動作、ループイテレーション数、CPU を観察します。イテレーションごとのモードによって、サンプリングのための余分なイテレーションが強制されたり、ループが不必要に維持されたりしてはなりません。

公開情報ソース

関連する質問

関連面接ツール

コーディング問題にはスクリーンショットを使用

問題をキャプチャし、制約条件、解法アプローチ、コード、エッジケース、計算量の順に進めます。

ツールを見る