負荷テストのモデリング:セッション、ペーシング、ユーザー行動

負荷テストには認識上の問題があります。いまだに、多くの場合「量」の演習として扱われています。つまり、ユーザー数はいくつか、リクエスト数はいくつか、スループットはどれくらいか、という考え方です。これらの数値は設定しやすく、報告しやすく、テスト間で比較しやすい一方で、不完全でもあります。

本番環境のシステムは、「ユーザー」を静的な数として認識しているわけではありません。時間の経過とともに発生する活動として認識しています。リクエストは均等には到達せず、セッションは重なり合います。ユーザーは一時停止し、再試行し、フローを離脱し、後になって戻ってきます。短く軽量なセッションもあれば、長時間継続し状態を持つセッションもあります。こうした動きは、単なるピーク時の同時接続数以上に、インフラの挙動を左右します。

ここで重要になるのが、負荷テストのモデリングです。流行語としてではなく、トラフィックが実際にどのように振る舞うのかを記述するための規律としてのモデリングです。セッション、ペーシング、ユーザー行動は、合成テストを信頼できるシミュレーションへと変えるための仕組みです。これらがなければ、よく実行された負荷テストであっても、一見安心できる結果を示しながら、現実の障害を予測できないことがあります。

負荷テストのモデリングはユーザー数の設定ではない

本質的に、負荷テストのモデリングとは、負荷がどのようにシステムに入り、どのように蓄積され、時間の中でどのように持続するかを定義する行為です。これは単なる設定作業ではなく、仮想ユーザー数の目標を選ぶことと同義でもありません。負荷モデルは、システムが受ける圧力のを記述します。その圧力がどのように変化し、重なり合い、活動の継続とともに増幅していくのかを含みます。

実際の環境では、負荷は均等にも瞬間的にも適用されません。波のように到達し、アクティブなセッションを通じて滞留し、アイドル期間中に一時停止し、再試行や再訪によって再び現れます。これらの動きは、リソースが短時間だけ使用されるのか、継続的に圧迫されるのか、内部状態が安定するのか徐々にずれていくのか、障害がすぐに顕在化するのか潜在したままになるのかを決定します。負荷モデリングは、これらの動きを偶然に任せるのではなく、意図的に捉えるために存在します。

負荷モデルは次のような問いに答えます。

  • ユーザーは時間とともにどのように到達するのか?
  • どのくらいの時間アクティブでいるのか?
  • どのような操作を、どの順序で行うのか?
  • 操作の間にどれくらいのアイドル時間があるのか?
  • いつ、そしてなぜ離脱するのか?

同じリクエスト量を生成する2つのテストであっても、これらの問いへの答えが異なれば、システムの挙動は大きく変わります。徐々に到達する短命なセッションが1000あるケースと、長時間接続され、認証され、状態を維持したままのセッションが200あるケースは同等ではありません。その違いは、メモリ使用量、コネクションプール、キャッシュの有効性、バックグラウンドタスクの負荷に表れます。

チームが同時接続数だけに注目すると、負荷は一瞬のスナップショットに還元されます。モデリングは時間という次元を取り戻します。そして、多くの実際の障害はその時間の中に存在します。

現実の単位としてのセッション

セッションは、時間の中で展開される意図を表します。ユーザーが実際にアプリケーションとどのようにやり取りするかに最も近い抽象化です。

セッションが重要なのは、状態が蓄積されるからです。認証トークンは発行され更新され、キャッシュは温まり、やがて劣化します。サーバー側のセッションストアは増大し、データベース接続は想定よりも長く開いたままになります。ステートレスなアーキテクチャであっても、繰り返されるアクセスパターンや共有リソースによって、セッションのような振る舞いが生まれます。

多くのシステムでは、障害はピークリクエストレートよりも、セッションの継続時間と強く相関します。メモリリーク、遅いガーベジコレクション、スレッド枯渇、コネクション不足といった問題は、短時間のスパイクではなく、持続的なセッション活動の後に表面化する傾向があります。

セッションを意識した負荷テストは、こうした挙動を明らかにします。バーストではなく連続性を管理することをシステムに強います。リソースが迅速に解放されているか、バックグラウンドのクリーンアップが追いついているか、性能が突然崩壊するのではなく徐々に低下しているかを示します。

セッションを無視すると、攻撃的に見えても運用上は浅いテストになります。セッションをモデリングすることで永続性が導入され、その永続性の中でシステムは正直に試されます。

ペーシング:時間は隠れた変数

ペーシングは、セッション内のアクションが時間の中でどのように分配されるかを定義します。思考時間、ステップ間の遅延、新しいセッションが開始されるレートなどが含まれます。

不適切なペーシングは、誤解を招く負荷テスト結果の最も一般的な原因の1つです。トランザクションを連続して実行する高速なループは、数時間分の実際の活動を数分に圧縮してしまいます。これにより、本番ではほとんど存在しない人工的な競合パターンが生まれ、同時に、持続的な圧力がなければ現れない時間依存の障害が隠されます。

同様に問題となるのが、過度に同期されたペーシングです。すべての仮想ユーザーが同時に行動すると、システムは非現実的なリクエストの整列を経験します。本番トラフィックはノイズを含んでおり、リクエストは不完全に重なり合います。ためらうユーザーもいれば、すぐに再試行するユーザー、完全にフローを離脱するユーザーもいます。

ペーシングは、オープン型とクローズド型のワークロードモデルも区別します。クローズドモデルでは、ユーザーは応答を待ってから次に進みます。オープンモデルでは、システムの健全性に関係なく到達が続きます。どちらにも正当な用途はありますが、生成されるストレスプロファイルは異なります。誤ったモデルを選ぶと、実トラフィックでは通用しない自信に満ちた結論に至る可能性があります。

正確なペーシングはテストを遅くしません。テストを引き伸ばします。その引き伸ばしによって、システムは急性の障害だけでなく、徐々な劣化を明らかにできるのです。

ユーザー行動がシステムの結果を形作る

ユーザー行動は、負荷の上に重ねられたランダムなノイズではありません。負荷そのものの構造です。

行動パターンが異なれば、システムに与えるストレスも根本的に異なります。読み取り中心の閲覧はキャッシュやCDNエッジを活用します。書き込み中心のトランザクションフローはデータベースやキューに圧力をかけます。アイドル状態のセッションはメモリやコネクションスロットを消費します。再試行行動は障害を平滑化するのではなく、増幅させます。

ごくわずかな行動の変化でも、結果は変わり得ます。レイテンシ下での再試行の積極性が少し増えるだけで、バックエンド負荷が倍増することがあります。セッション時間がわずかに長くなるだけで、キャッシュが有効容量を超えることもあります。離脱が増えると、クリーンアップ経路を完了しない部分的な状態が残されることもあります。

行動のモデリングは、チームにこれらの現実と向き合わせます。負荷テストを理想化されたフローから、実際の利用で観測される雑然としたパターンへと移行させます。すべてのエッジケースをシミュレートする必要はありません。支配的な行動を特定し、それらが時間の中で自然に相互作用するようにすればよいのです。

システムが失敗するのは、ユーザーが完璧に振る舞うからではありません。ユーザーが現実的に振る舞うからです。

持続負荷とピーク負荷

ピーク負荷テストは有用です。限界を見つけ、システムが完全に応答しなくなる地点を示します。しかし、多くの本番障害は、それらの限界よりはるかに低いところで発生します。

持続負荷は、別の種類の問題を明らかにします。ゆっくりだが無制限に増え続けるメモリ使用量、作業セットの変化とともに劣化するキャッシュ、流入よりも排出が遅いキュー、最初は正しく反応するが時間とともにうまく機能しなくなるオートスケーリング挙動などです。

これらの問題は、短く攻撃的なテストでは表に出ません。現実的なセッションの重なりとペース配分された活動が数時間続いた後に現れます。本番で表面化した時点では、しばしばアーキテクチャの挙動ではなく、「トラフィックの異常」と誤って片付けられます。

負荷テストのモデリングは、持続テストを実用的で意味のあるものにします。テスト時間を、システムが実際に失敗する時間軸に合わせます。

本番に一致する負荷モデルの設計

効果的な負荷モデルは、仮定ではなく観測に基づいて構築されます。

本番の分析データ、アクセスログ、APMデータは、到達レート、セッション長、一般的なパスを明らかにします。ユーザーがどこで一時停止し、どこで再試行し、どこでフローを離脱するのかを示します。これらのシグナルは、モデリングの判断に直接反映されるべきです。

実践的なアプローチは、代表的なセッションタイプを少数特定することから始まります。各セッションタイプは、フロー、継続時間の範囲、ペーシング特性を定義します。到達レートは、それらのセッションがどのように重なり合うかを決定します。アイドル時間や離脱も、後付けではなく意図的に含めます。

モデルは現実と照らし合わせて検証する必要があります。セッション時間やスループットが観測データと大きく乖離している場合は、モデルを調整すべきです。目標は秒単位の正確さではありません。システムレベルでの忠実度です。

負荷モデリングは反復的なプロセスです。アプリケーションが進化すれば、行動も変化します。テストもそれに合わせて進化しなければなりません。静的なモデルは、正当化されにくい静的な信頼しか生みません。

LoadViewを用いた負荷テストモデリングの適用

負荷モデリングには、状態、タイミング、行動を第一級の要素として扱うツールが必要です。実際のブラウザベースのテストは、セッションの連続性を維持し、クライアントサイドレンダリング、JavaScript実行、ネットワーク競合を含む現実的な実行経路を強制することで、これを可能にします。これらの制約は、人工的な遅延に頼ってユーザー行動を近似するのではなく、自然にペーシングやインタラクションのタイミングを形作るために重要です。

LoadViewのスクリプト化されたユーザーフローは、思考時間、アイドル期間、再試行行動を明示的に制御しながら、複数ステップのインタラクションにわたってセッションを持続させることを可能にします。シナリオベースのテストにより、複数のセッションタイプを単一のテスト内で同時に実行でき、長命な行動と短命な行動を、本番トラフィックを反映した比率で重ね合わせることができます。持続負荷や段階的負荷の設定により、ピーク需要時だけでなく、圧力が時間とともに蓄積し持続する際のシステムの反応を明らかにします。

価値は、より多くの負荷を生成することにあるのではありません。正しい負荷を生成することにあります。

結論:負荷テストはモデリングの規律である

負荷テストの成否は、最初のリクエストが送信される前に決まります。それはモデルの中で決まるのです。

セッション、ペーシング、ユーザー行動は、負荷がシステム内部でどのように現れるかを決定します。これらは、メモリ使用量、コネクションの寿命、キャッシュの有効性、障害モードを形作ります。それらを無視すると、見た目は印象的でも、予測力の乏しいテストになります。

成熟したパフォーマンステストは、負荷モデリングを第一級の規律として扱います。攻撃性よりも現実性を、スナップショットよりも時間を重視します。モデリングに投資するチームは、単に早く障害を見つけるだけではありません。より深く理解できるのです。

最終的に、システムはユーザー数に反応するのではありません。時間の中で展開される行動に反応します。負荷テストも同じであるべきです。