72. トレースIDはリレーのバトンである — 相関IDが分散システムに因果の鎖を通す
1本のリクエストが複数のサービスをまたいで走るとき、X-Rayがどうやって「これは全部同じリクエストだ」と束ね直すのかを、IDがヘッダで運ばれていく様子から図で追っていきます。
① なぜ「後から突き合わせる」しかないのか
- 各サービスは独立したプロセスとして動く。疎結合になるほど全体像が見えなくなる問題が、ログの世界でそのまま現れる。
- プロセスが分かれている以上、後からログを突き合わせる以外に因果を復元する手段がない。
バラバラのプロセスが残すログを1本のリクエストとして再構成するには、全員が同じ目印を書き残すしかない。これが相関ID(correlation ID)というCS基礎で、X-Rayのすべての仕組みはこの1点に立っている。
② 入口でバトンが生まれる — トレースIDの割り当てと伝搬
- 公式の表現では「The first supported service that the HTTP request interacts with adds a trace ID header to the request, and propagates it downstream(最初にリクエストと接触したX-Ray対応サービスがトレースIDヘッダを追加し、下流へ伝搬する)」。
- ヘッダ名は X-Amzn-Trace-Id。SDKがこのヘッダを読み、レスポンスにも含める。
- ヘッダの例: X-Amzn-Trace-Id: Root=...;Parent=...;Sampled=1
トレースIDはリレーのバトンだ。入口で1本だけ生まれ、あとは各走者(サービス)がHTTPヘッダという手渡しで次へ運び続ける。バトンを運ぶ手法そのものは、API Gateway編で見た「ヘッダはメタデータの通り道」の応用にすぎない。
③ バトンの中身 — ヘッダの3フィールドとIDに埋まった時刻
- 公式仕様では trace_id は「ハイフンで区切られた3つの数」。(1) バージョン番号は 1、(2) 元リクエストの時刻をUnixエポック秒で16進8桁、(3) 96ビットのグローバル一意識別子を16進24桁。
- Parent は呼び出し元セグメントのID(64ビット=16進16桁)。Sampled は 0/1 のサンプリング決定。
- セキュリティ注記: trace_id はレスポンスヘッダで見えるため、将来のIDを攻撃者に計算されないよう「secure random algorithm」で生成すること、とドキュメントは明記する。
- Lambda 等が Lineage=... を追記することがあるが、これは内部処理用で直接使うべきではない。
バトンには「どのリクエストか(Root)」「誰から渡されたか(Parent)」「記録に残すか(Sampled)」の3つが書かれている。RootのIDそのものに生成時刻が埋め込まれているのが巧妙な設計で、DynamoDB編で見た「キーの中に情報を持たせる」発想と同じく、IDが単なる乱数ではなく意味を運ぶ。
④ 各サービスは「セグメント」を送り、X-Rayが同じIDで束ね直す
- 公式定義「X-Ray receives data from services as segments. X-Ray then groups segments that have a common request into traces」。
- セグメントが記録するのは、ホスト(hostname/IP)、リクエスト(method・client address・path・user agent)、レスポンス(status・content)、作業(開始/終了時刻・サブセグメント)、発生した問題(errors/faults/exceptions、例外スタックの自動キャプチャを含む)。
- セグメントドキュメントは最大64 kB。最小構成でも name・id・trace_id・start_time、そして end_time(処理が進行中の間は代わりに in_progress: true)が必須。
各走者はゴール後に自分の走行記録(セグメント)を提出するだけ。X-Ray側が「同じトレースIDのものは同じリクエスト」という一点で全記録を束ね直し、1本のトレースとサービスマップに再構成する。束ねる鍵がトレースIDなのは、DynamoDB編のパーティションキーが「同じキーは同じ場所」を決めたのと同じ原理だ。
⑤ セグメントの中を刻む — サブセグメントと木構造
- サブセグメントは「downstream callsのより細かいタイミング情報と詳細」を提供し、AWSサービス呼び出し・外部HTTP API・SQLデータベースのいずれかを記録できる。
- 任意のコードブロックを囲む独自サブセグメントも定義可能。サブセグメントはサブセグメントを含められる(ネスト)。
- id は各サブセグメントも64ビット=16進16桁。
セグメントを内側から下流呼び出しごとに刻んだものがサブセグメント。親子でぶら下がる入れ子構造は、そのまま木構造であり、これを辿ればリクエストが呼び出したサービスのコールツリーが復元できる。「ばらばらのログ」が、時間軸を持つ1本の木に組み上がる瞬間だ。
⑥ トレースを送らない相手も地図に現れる — 推測セグメント
- 公式「For services that don't send their own segments, like Amazon DynamoDB, X-Ray uses subsegments to generate inferred segments and downstream nodes on the trace map」。
- もし下流も計装済みなら、下流が送る本物のセグメントが推測セグメントを置き換える。その場合、ノードは下流サービス自身のセグメント情報を、エッジ(2ノード間の線)は上流のサブセグメント情報を使う。
- 上流視点は往復レイテンシ(移動時間込み)を、下流視点は正確な作業開始/終了を記録する。
トレースに対応していないサービスでさえ、呼び出した側の記録から逆算して地図に描かれる。バトンを持てない相手の区間も、渡した側の記憶から復元する——これで依存関係の地図に穴が空かない。
⑦ 検索・偽造・寿命 — 運用の3つの約束事
- アノテーションは「filter expressionsで使うために索引されるkey-valueペア」で、X-Rayは1トレースあたり最大50個を索引する。
- メタデータは「索引されない」key-valueで、値は任意の型(オブジェクト/配列可)。
- セキュリティ: トレースヘッダはX-Ray SDK・AWSサービス・クライアントリクエストのいずれからでも来うるため、「Your application can remove X-Amzn-Trace-Id from incoming requests(アプリは受信リクエストからこのヘッダを除去できる)」——ユーザーが勝手にトレースIDやサンプリング決定を付けてくる問題を避けるための定石。
- トレースデータ・サービスグラフデータともに保持は30日。
索引される検索用の目印(アノテーション、50個まで)と、索引されない記録用の付箋(メタデータ)を使い分ける。外部から来たバトンは信用せず入口で剥がし、集まったトレースは30日で消える。相関IDの仕組みは、検索性・信頼境界・保持期限という運用の現実と一緒に初めて完成する。