Article by: David Rosenthal
2年前、Sentry は「紙の上では」完璧に見えるメトリクス製品を作りました。ところが、自分たちでドッグフーディングしてみると、それが本当にお客様の求めているものではないことに気づいたのです。ローンチ予定の 2 週間前、私たちはそのプロダクト全体をお蔵入りにしました。
ここでは、その過程で得た学び、なぜ従来型の時系列メトリクスがモダンなアプリケーションのデバッグでは破綻してしまうのか、そして私たちがそのシステムをどのようにゼロから作り直したのかをお話しします。
最初のメトリクス製品がイマイチだった理由
2年以上前、Sentry では開発者向けのメトリクス製品をつくろうと動き始めました(私が Sentry に入社する前のことです)。その結果、ほとんどのオブザーバビリティプラットフォームが通ってきた道、メトリクスを事前に集計し、時系列データとして保存するというアプローチを選ぶことになりました。この方法であれば、エンドポイントのレイテンシやリクエスト数といった指標を効率よく、かつ高速に追跡できるはずでした。
そして実際、私たちのチームは成功しました。個々のメトリクスを追うだけなら、高速で低コストに動いていたのです。
しかし Sentry には、社内で自分たちのプロダクトを徹底的に使い込む「ドッグフーディング」の文化があります。この仕組みを実際の現場で使い始めたとき、すぐに問題点が見えてきました。それは、同じ設計思想のメトリクス製品が必ずぶつかる古典的な課題、いわゆる Cartesian product problem(デカルト積問題) でした。
ご存じない方のために説明すると、従来型のメトリクスでは新しい属性を 1 つ足すたびに、その属性の値ごとに新しい時系列データを保存しなければなりません。たとえば "server" 属性にサーバー名を入れるなら、サーバー名ごとに別々の時系列が必要になります。さらに複数の属性で分解したければ、それらの値のすべての組み合わせに対して時系列を持たなければなりません。これはすべて「事前集計」をする以上、あらかじめ「どんな問いを立てるのか」を決めておく必要があるという根本的な制約から生じています。
もちろん、これは常に大問題になるわけではありません。30台のサーバーのメモリ使用量を追跡するとか、その 16 コアそれぞれの CPU 使用率を見る、といった用途だけなら、従来のやり方でも十分に機能します。
しかし Sentry のゴールは、「コードが壊れたときに、開発者がデバッグして修正するためのリッチなコンテキストを提供すること」です。そのために、開発者は状況を理解しやすくするための属性やコンテキストをできるだけたくさん付けたくなります。ところが、コンテキストを増やせば増やすほどコストが爆発していくとなると、開発者は「本当は取りたいコンテキスト」を諦めざるを得ません。
社内でこのプロダクトを使っていたエンジニアたちは、常に「欲しいコンテキスト」と「払えるコスト」のあいだで板挟みになっていました……。これは、明らかに良くない状態でした。
カーディナリティという女王様
例を見てみましょう。
仮に、個々のタイムシリーズを保存するコストが 1 本あたり月 $0.01 だとします(業界的にもだいたいこのオーダー感です)。そして、ある特定のエンドポイントのレイテンシを追跡していて、そのエンドポイントは 1 日あたり 10 万回呼ばれているとしましょう。ここまではよくある状況です。
ここに、開発者 A がやってきて、「どのサーバー(8 台のうちどれ)で処理されているかを知りたい」と考え、そのための属性を 1 つ追加します。
次に開発者 B が、「どの顧客アカウント(200 件のうちどれ)向けのリクエストなのかを知りたい」と言い出します。
さらに開発者 C は、「どのサブリクエスト種別(12 種類のうちどれ)なのかも知りたい」と考えます。
そして最後の「決定打」として、開発者 D が「どのユーザー(5,000 人のうちどれ)なのかも見たい」と言い出したとしましょう。
では、この時点でコストはいくらになっているでしょうか?答えは、およそ 月 100 万ドル です。はい……これが現実の数字です。
正直に言うと、これはまだ「やさしい」例です。実際の現場では、10〜20 個の属性を記録したい、というのはまったく珍しくありません。もちろん、「特定の組み合わせのフィルターは避ける」など、コスト爆発を抑えるためのテクニックはいろいろありますが、そのやりくり自体が相当な負担になります。
要するに、私たちはこのままでは「ユーザーにとってつらいトレードオフ」を大量に押し付けることになると確信するに至りました。とくに可能な限りリッチなデバッグコンテキストを提供したいという Sentry のミッションにとっては致命的です。
そして最終的には、リリース予定日の 2 週間前(そして、私が入社して 2 週間後)に、そのプロダクトのスイッチを切る決断をしたのは私でした。正直に言うと、そのとき私はあまり人気者ではありませんでした。
こんなことも予測できなかったの?
「いやいや、お前らバカなの?こんなの最初から分かってた話じゃないの?」そう言いたくなる気持ちは分かります。実際、その通りです。
メトリクスプロジェクトを立ち上げた当初から、この点についてはかなり激しい議論がありました。本来のビジョンは、「ユーザーがすでに持っているトレーシングのテレメトリとメトリクスをつなぐこと」にありました。
ところが、スコープのブレ、コミュニケーション不足、コスト構造への過度な不安、「周りと違うやり方をすること」への恐れなどが重なり、その過程でいくつもの妥協が入り込んでしまいました。
この経験から、痛いほどはっきりと再確認することになりました。新しいプロダクトをつくるときは、最初に掲げたビジョンを明確にし、そのビジョンから外れたり、安易に妥協したりしないように細心の注意を払わなければならないということです。
なぜビジョンは「トレースとつながること」だったのか
さっきまで「問題はコストの爆発だ」と言っていたのに、今度はトレースとつなぐ話?と思われたかもしれません。実は、v1 の問題はもっと根深いところにありました。
一旦、カーディナリティやコストの話は脇に置いて、「計算資源は無限にある」と仮定してみましょう。時系列メトリクスには、もうひとつ大きな問題があります。コードの中で increment() や gauge() をあちこちで呼び、それらを 1 秒ごとの粒度で集計するというやり方そのものがよくないのです。なぜなら、その時点で「データ」と「コード」のつながりが失われてしまうからです。
もちろん、メトリクスを時間軸でグラフ化すること自体はできます。でも、そのグラフはあくまで「並行世界」に存在しています。あなたのコード(やトレース、ログ、エラーなど)とは、タイムスタンプを手がかりに、間接的にしか結びついていません。
ここに問題があります。繰り返しになりますが、私たちのゴールは「開発者にダッシュボードをあげること」ではなく、「コンテキストを渡すこと」です。何か問題が起きたときに、「なぜそうなったのか」が見える、直接的で行動につなげやすく、コードときちんと結びついたコンテキストを渡すことが目的なのです。
タイムスタンプを手がかりにした「間接的な」相関づけでは、それがうまくできません。レイテンシのスパイクやエラー率の上昇をグラフで見てから、別システムに切り替え、トレースやログを漁りながらタイムラインを頭の中でつなぎ合わせていく。こうした「だいたいこの時間帯で何かあったはず」式のデバッグは、本当に頭を抱えることになりますし、これは従来型メトリクスのもうひとつのアキレス腱でもあります。
では、お客様は本当に「ベストなやり方」を選べるのでしょうか。
お客様は本当に「ベストなやり方」を選べるのか
ではカーディナリティが爆発せず、かつ 1 秒単位への集計にも頼らないシステムとは、どのような姿になるのでしょうか。
「デイブ、その方向に話が進むってことは、きっと高くつくんでしょ…?」という声が聞こえてきそうです。はい、すべての生データを保持しつつオンザフライで集計するにはどれくらいコストがかかるのか、そしてどんなトレードオフがあるのか、という話をしていきます。
その前に少しだけ背景を説明します。多くのオブザーバビリティ/アナリティクス系システムは、すでに「事前集計」から離れ、イベントを生のまま保存し、必要になったときに集計する方式へと移行しています。これは、ストレージの低コスト化、大規模並列計算の発達、カラム型クエリエンジンの台頭によって可能になったものです。この大型トレンドは 2000 年代初頭の Hadoop から始まり、その後ゆっくりと業界全体に広がってきました。
ログ、トレーシング、BI(OLAP キューブを覚えていますか?)、さらにはセキュリティといった領域でも、初期のツールは事前定義された集計に大きく依存していましたが、いまではほぼ例外なく生データを保存し、オンデマンドで分析する形に切り替わっています。では、次はいよいよ「メトリクスの番」なのでしょうか。
先ほどのエンドポイントレイテンシの例に戻りましょう。すみません、また計算の話です…。先ほどの例で使った属性(server、customer、sub type、user email)をすべて含めた生データのサイズが 1 件あたり 250 バイトだとします。そして、コストの目安として Sentry Logs の料金($0.50/GB/月)をそのまま使ってみます。
1 日あたり 10 万回の記録が発生すると仮定すると、月間コストは……約 $0.37/月 になります。そう、これ自体は「個々のタイムシリーズ 1 本を追跡するコスト($0.01/月)」に比べれば 0.36ドル高いのですが、先ほどのように 4 つの属性すべてについて時系列を爆増させた場合の 約 100 万ドル/月 に比べれば、はるかに現実的です。
ここからの教訓はこうです。メトリクスベンダーの営業担当は別のことを言うかもしれませんが、「指数関数の間違った側」に乗ってしまうのは絶対に避けるべき、ということです。
鋭い方はすでにお気づきかもしれませんが、ここで 1 日 10 万回という前提も重要な変数です。従来型のメトリクスでは、この回数が増えてもコストはほとんど変わりませんが、生データベースのメトリクスでは、もちろん回数に応じてコストが増えます。
つまり、もし 1 日あたり 100 万回・1,000 万回発火するメトリクスであれば、すべての生データを保持した場合のコストは、それぞれ $3.70/月、$37/月 になります。1 つのメトリクスとしては決して安くはありませんが、ユーザー向けの重要なエンドポイントが 1 日 1,000 万回呼ばれているのであれば、正直「おめでとうございます」と言っていい規模です。
もしコストを抑えたいのであれば、サンプリング(私のお気に入りの手法です)を導入することもできますし、もう少し上位レイヤーの集約カウンタだけをログに記録する、という手もあります。
「サンプリングとか、情報が欠けるやり方は大嫌いだ」という方もいるかもしれません。しかし残念なお知らせとして、もし 1 日 1,000 万回リクエストが来ているエンドポイントがあるなら、それは平均して毎秒 100 回以上リクエストが飛んでいる計算になります。そして、1 秒バケットのタイムシリーズは、その時点ですでに膨大なディテールをざっくりと押しつぶしているのです。
トレースとつながったテレメトリでゼロから再出発
話を元に戻しましょう。私たちはスイッチを切りました。その後にやるべきことはシンプルでしたが、実際には時間のかかる道のりになりました。私たちはいったん白紙に戻り、「イベントベースのメトリクス」を構築するところからやり直すことにしたのです。
この決断は、Sentry 全体のより大きな再アーキテクチャにつながり、社内で Event Analytics Platform(EAP) と呼んでいる新しい汎用テレメトリアナリティクス基盤の誕生へと発展しました。これは、モダンなカラム型ストアである ClickHouse の上に構築されています。すでに何度かこの仕組みについての登壇もしていますが、本来であれば EAP だけで 1 本ブログを書けるくらいの内容です。
まず私たちは、既存のトレーシングプロダクトを強化するために EAP を導入し、新しい絞り込みやクエリ機能を実装しました。次に、最近リリースしたログプロダクトの基盤としても活用しました。そして、メトリクス 1.0 のスイッチを切ってから 18 か月以上を経たいま、EAP は次世代の Metrics プロダクトを支える土台になっています。
今回の Metrics で新しいのは、すべてのメトリクスイベントを EAP に個別のイベントとして保存し、それぞれをトレース ID と結びつけているという点です。これによって、カーディナリティの問題と「つながり」の問題の両方を解消しています。メトリクスをあとから自由にスライス・ダイスし、他のテレメトリとリンクさせることができるようになったのです。さらに、カーディナリティの高いタグも含めて、コンテキスト用のタグをどれだけ追加しても、コスト爆発を気にする必要がなくなりました。
コンテキストを備えたメトリクス
私たちは再びドッグフーディングを始めましたが、今回はかなり手応えがあります。トレースとつながったこのモデルによって、これまで不可能だったデバッグワークフローが実現できるようになりました。ダッシュボードを行き来して「この時間帯かな?」と当たりをつけるのではなく、データそのものをたどれるようになります。
例えば、次のようなことができます。
- チェックアウト失敗のスパイクに気づいたとき:そのメトリクスから、実際に失敗が発生したトレースへジャンプし、さらにそのトレースから紐づいた Sentry のエラーへ飛ぶことができます。
- リトライ回数の増加に気づいたとき:メトリクスをスパンごとに分解し、どのサービスが原因になっているのかを特定できます。
- p95 レイテンシがじわじわ上がってきたとき:最も悪化しているケースを深掘りし、関連するセッションリプレイを確認することで、ユーザーが実際にどんな体験をしているのかまで把握できます。

アプリケーションのためのメトリクス
インフラ監視はどうなの?という疑問もあると思います。正直に言うと、そういう用途に Sentry Metrics を使うユーザーもいるだろうとは考えていますが、私たちとして積極的に推したいユースケースではありません。
この点については社内でもかなり議論がありました。というのも、「メトリクス」という言葉を「インフラ」と切り離して考えるのが、それだけ難しいからです。マシンレベルのメトリクスはもともとコードから切り離されているので、インフラの世界では時系列の事前集計はとても魅力的に見えます。「安くできそうだし、この小さなデーモンを動かせば、いろんなシグナルを一気に吸い上げて、ほら、グラフもすぐ出せる」といった具合です。

正直に言うと、私たちはインフラメトリクスを使用しており、それがなくなることはないと認識しています。しかし、私たちにとってメトリクスは、アプリケーションレベルのシグナル、つまり、それを生成した基盤コードと結びついたシグナルである場合に、はるかに強力になります。従来のCPUやメモリのグラフにも一定の役割はありますが、現代の開発者、特に高レベルのプラットフォーム抽象化に基づいて開発を行う開発者は、インフラよりも、ログイン失敗、決済エラー、リクエストのレイテンシといった高レベルのアプリケーションの健全性を重視しています。こうしたメトリクスこそが、ユーザーに実際に何が起こっているかを教えてくれるのです。
Seer のために生まれたメトリクス
せっかく CTO を名乗っている以上、AI の話を一度もせずにブログを終えるわけにはいきません。そして、メトリクスの作り直しを決断したときには、AI まわりの取り組みはまだ立ち上がりかけの段階でしたが、結果的には「AI でのデバッグを本気でやる」という意味でも正しい選択だったことが分かりました。
ご存じない方のために言うと、Sentry には Seer というエージェントが組み込まれています。Seer は問題の根本原因を特定し、修正案を提案し、今後はさらに多くのことを担うようになる予定です。
裏側では、Seer はツールコールを使って Sentry 内のあらゆるテレメトリ(エラー、トレース、ログ、そして新しいメトリクス)を横断しています。テストを重ねる中で分かってきたのは、「大量のデータ(100万件のサンプル)」を持つこと自体は、Seer にうまくデバッグさせる上でそこまで重要ではないということです。それよりも、新しい種類の「つながったデータ」を 1 つ追加するたびに、大きな効果が得られる という事実のほうがずっと重要でした。メトリクスをテレメトリグラフの「第一級の住人」として扱うことで、Seer が利用できる接続されたコンテキストが一気に増えたわけです。
このことは、「最初に掲げたビジョンにこだわり続けることの大切さ」をチーム全体で再確認する良い実例になったと思います。当初掲げていた「すべてをトレースとつなげる」という方針は、メトリクスを従来型の別機能として出してしまうより、結果的にずっと大きなリターンをもたらしましたし、その時点ではまだはっきりイメージできていなかった新機能(Seer のような AI 機能)にとっても、大きな追い風になったのです。
時間をかけて作り直した価値はあった
ここまで読んでくださっている方なら、いったん作ったプロダクトに「もうやめよう」と判断を下すのが、どれだけ難しいかはよくご存じだと思います。ちゃんと動いているものであればなおさら、「これは正しいものではない」と認めるのはもっと難しい。サンクコストバイアスは本当に手強い相手です。Metrics v1 を気に入って使ってくださっていたベータテスターのみなさんには申し訳ない気持ちでいっぱいですが、それでも新しいバージョンのために待っていただく価値はあったと私たちは確信しています。
今回のブログは、いわゆる「新製品リリースのお知らせ」という意味では、かなり変則的なマーケティング記事だったと思います。ただ、今回はあえて少し違う切り口をとったからこそ、その裏側にある考え方を共有するのもおもしろいのではないかと思いました。
これからもこの新しい Metrics プロダクトを育て、改善し続けていくなかで、ぜひ一度試してみていただければうれしいです。そして同時に、このストーリーが、みなさん自身のソフトウェアプロジェクトにおいても、「つらい決断を恐れない」ための、ちょっとしたヒントや励ましになれば幸いです。
Original Page: The metrics product we built worked — But we killed it and started over anyway
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談はこちらのフォームからお気軽にお問い合わせください


