「8% 遅くなった」にはノイズの大きさが抜けている
一言でいうと
2つの実行の差が回帰かどうかは、差の大きさだけでは決められません。何も変えなかったときに出る差、つまりノイズの大きさを、先に測る必要があります。
なぜ必要なのか
デプロイパイプラインに性能テストを付けたチームがありました。ルールは簡単でした。直前のデプロイよりp50が5%を超えて遅くなったら、デプロイを止める。もっともらしく見え、実際に最初の週に3回止めました。
問題は、その3回のうち2回が誤検知だったことです。元に戻した変更の1つは、ログを1行消しただけで、もう1つはコメントの修正でした。あとで何も変えていないコミットで、同じテストを5回回してみると、p50が7%まで開きました。しきい値の5%は、ノイズより小さかったのです。
誤検知は、それ自体もコストですが、より大きな被害は、信頼を削ることです。ゲートが頻繁に空回りすると、人はゲートを切るか、「再試行」を押す方法を学びます。そして本物の回帰が通り過ぎるとき、誰も止めません。
逆に、しきい値を緩くしすぎると、ゲートは何も止めないのに、止めているという錯覚だけを与えます。この2つの失敗は、同じ根から出ています。しきい値を決めるとき、ノイズを測らなかったことです。ノイズを測るのにかかるコストは、同じテストをあと何回か回す時間だけで、その時間は、誤って元に戻した変更1つを調査する時間より、常に短いのです。
どう動くのか
判定に必要なのは、3つです。何を測るか、何回も測った値をどう1つにまとめるか、そしてその1つを何と比べるか。
何を測るか。平均は裾に引きずられ、最大値は1回の事故に左右されます。p50は安定していますが、裾が見えず、p99は裾が見えるものの、サンプルが少ないと激しく揺れます。ゲートには揺れの小さい値を使い、裾は別に監視するほうがよいでしょう。
何回も測った値をどうまとめるか。1回測って終わりにすると、その1回が運の悪い実行だったかどうか、知る方法がありません。そのため、同じ条件で何回も回して、その値の中央値を使います。中央値は、1回のおかしな実行に引きずられません。
何と比べるか。ここが核心です。しきい値を「5%」のように固定すると、その数字がどこから来たのか、誰も知りません。代わりに、ベースラインを繰り返し測った値のばらつきを、そのまましきい値として使います。最も単純な形は、最大から最小を引いた値で、より頑丈にするなら、中央絶対偏差の何倍かを使います。どちらにせよ、しきい値が、そのとき、そのマシンで実際に測った値であるという点が重要です。
このルールには、自然な性質が1つあります。反復回数を増やせば、ノイズの推定が良くなって、より小さな回帰を見つけられ、反復を1回に減らすと、しきい値が0になって、どんな差でも回帰として報告します。そのため「何回回すか」は、時間と感度のトレードオフを決める判断であって、好みではありません。
現場での姿
最もよくある失敗は、ベースラインを1回しか測らないことです。昨日のデプロイの測定値1つをベースラインにして、今日の値1つと比べると、しきい値を決める材料がそもそもありません。このとき人は、固定のパーセンテージを持ち出し、その数字はたいてい、会議室で決まります。
2つ目は、条件が変わったことを回帰と読む場合です。ベースラインは暇な明け方に測り、新しい測定は他の作業が一緒に動く昼に測ったなら、2つの数字は、そもそも比べられる組ではありません。同じマシン、同じ時間帯、同じ負荷の形で、交互に測って初めて、ノイズの定義が成り立ちます。
3つ目は、ベースラインが古くなっていくことです。ベースラインを一度測っておいて、何か月もその値とだけ比べると、その間にカーネルが上がり、基盤ライブラリが変わり、マシンが交換されます。そのときのノイズと今のノイズは別の値で、古いベースラインとの差は、回帰ではなく、時間の経過です。ベースラインは、新しい測定と同じ日に同じマシンで、一緒に測り直すのが安全です。
4つ目は、ルールなしに目で判定する場合です。2つの棒グラフを並べて「確かに遅くなったね」と言った瞬間、その判定は再現できないものになります。次の人が同じデータを見て別の結論を出しても、誰も間違いだとは言えません。ルールをスクリプトに書いておけば、判定が記録として残り、しきい値が間違っていたとあとでわかったとき、何を直すべきかも明確になります。そしてそのスクリプトは、人が読むレポートではなく、終了コードを出す必要があります。パイプラインが読めて初めて、実際に何かを止められるからです。
5つ目は、回帰ではなく改善を見逃す場合です。ゲートを一方向にだけかけておくと、性能が良くなったこともわかりません。良くなった側の変化がノイズを大きく超えているなら、それも記録する必要があります。たいていは良い知らせですが、ときには、テストが仕事を飛ばし始めたというサインだからです。
次のラボですること
基本のレイテンシに、再現可能な揺れを載せた対象を起動し、同じ条件で5回測って、ノイズの大きさを先に求めます。そのあと、レイテンシを5%増やしたバージョンと、50%増やしたバージョンを、それぞれ5回ずつ測り、5回の実行の中央値どうしを比べるルールを立てて、どちらがノイズを超えているかを判定します。ベースラインの5回の実行のうち、最も速いものと最も遅いものを選んで、1回だけの比較を真似てみると、同じコードどうしでも、ゲートが回帰を報告することを、数字で見ることになります。最後に、ベースラインのディレクトリと新しい測定のディレクトリを受け取って、終了コードを出すゲートのスクリプトを作り、採点ツールが、自分で作った3セットの入力で、そのゲートを回して、合格と失敗の両方を確認します。