Observer Effect
はじめてSentryの導入提案を聞いたとき、正気かと思った。
誤解しないでほしい。Sentryが悪いと言いたいわけではない。エラートラッキングは必要だし、便利なのもわかる。ただ「入れましょう、簡単です、SDK入れるだけです」という空気に、危うさを感じた。
アプリケーションの中から、例外が起きるたびに、ネットワーク越しに外部サービスへイベントを送る。正常時はいい。例外なんてたまにしか起きない。問題は異常時だ。
障害が起きると例外が増える。例外が増えると観測イベントが増える。イベントが増えるとSDKがTCPで送信する。コネクションが増え、TIME_WAITが積み上がり、再送と輻輳制御が遅延を増幅させる。バックプレッシャーがアプリに伝播し、スレッドが枯渇し、本体のサービスが劣化する。一次障害が二次災害を引き起こす。観測が、システムを壊す。
量子力学に「観測者効果」という概念がある。観測という行為そのものが、対象の状態を変えてしまう。ソフトウェアの監視も同じ構造を持っている。正常時は無害な観測が、異常時にはシステムの振る舞いそのものを変える。しかも悪い方向に。
観測システムは「正常時の補助機能」ではない。「異常時にトラフィックが最大化する逆特性システム」だ。火災報知器が火元になるようなものだ。
根本的な問題は、観測処理がリクエストのホットパスに乗っていることだ。フロントエンドの世界では、テレメトリはbeaconやfire-and-forgetで送る。失敗しても捨てる。UXを最優先する。ところがサーバーサイドでは、awaitで送信し、同期HTTPで叩き、失敗したらリトライする。フロントでは当たり前に避けている設計を、サーバーでは平気でやっている。
観測はベストエフォートでいい。可観測性より可用性が優先だ。異常時にはデータを減らすべきだし、観測はRPCではなくストリームであるべきだし、何よりアプリから隔離されていなければならない。ダイナミックサンプリング、レート制限、サーキットブレーカー、ローカルのリングバッファ。これらは「あれば嬉しい機能」ではなく、観測システムの必須要件だ。
アプリからは非同期のロックフリーバッファにだけ書く。別プロセスのローカルエージェントが拾い、軽量なIngestに流し、ドロップ前提でストリームに乗せる。全部集めてから分析するのではなく、落としながら重要なものだけ残す。ログは捨てていい。サービスは止めるな。
Sentryを使うなとは言わない。ああいうツールがなかった時代を知っているから、ありがたみはわかる。ただ、SDK一行で入る手軽さが、設計の判断を省略させる。どこで、どう、どれだけ送るのか。障害時に何が起きるのか。その問いを飛ばして「とりあえず入れておこう」で済ませる現場の空気が怖い。
安全のための仕組みが、設計を誤ると障害の増幅装置になる。最優先すべきは精度ではなく、生存性だ。
結局その案件では、ベテランのSREが「障害時のコネクション増加と輻輳が気になる」と止めた。フロントだけに絞り、サーバー側はローカルエージェント経由の構成に落ち着いた。わたしが口を挟む前に片付いた。
5、6年前の話だ。いまのSentry SDKにはサンプリング、レート制限、バックオフが標準搭載されている。当時わたしが心配していたことの多くは、すでにSDK側で対処されている。
老婆心だったかもしれない。でも老婆心で済むなら、それがいちばんいい。