OTelのparentbased_traceidratio、SQS経由のLambdaだと親を継承しない

  • AWS
  • Lambda
  • OpenTelemetry
  • Observability

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

Lambdaのトレースサンプリング設定をIoT系のAPIとバッチ処理向けに整理してて、parentbased_traceidratio の挙動を裏取りしてたら、思ってたのと違うところがあった。

何が起きたか

OTEL_TRACES_SAMPLER=parentbased_traceidratio は、親のサンプリング判断があればそれを継承し、なければ OTEL_TRACES_SAMPLER_ARG の確率で判断する。ここまでは仕様通り。

問題は「親のサンプリング判断」がどこから来るかで、トリガーの種類によって全然違う。

API Gateway → Lambda

これは自動で継承される、と思い込んでたけど正確じゃなかった。継承されるのは、呼び出し元が実際に traceparent(W3C Trace Context)ヘッダーを送ってきて、それがAPI Gateway経由でLambdaまで届き、かつLambda側のHTTP計装がそのヘッダーを抽出してコンテキスト化してる場合だけ。

呼び出し元が何もヘッダーを付けてこなければ、Lambdaの呼び出し自体がルートスパンになる。つまり結局 traceidratio が直接効く。

AWS X-Rayのアクティブトレーシング(Root=...;Parent=...;Sampled=...)を使うパターンもあるけど、これはOTelのW3C伝播とは別の経路(AWS X-Ray Propagator)なので、混同すると話がおかしくなる。

SQS → Lambda

こっちは最初からある程度予想通りで、親コンテキストは渡らない。OTelのAWS Lambdaセマンティック規約を見ると、SQSイベントのスパンの親はLambda呼び出し自体のSERVERスパンであるべきとされてる。プロデューサー側の AWSTraceHeader は「リンク」として付与されるだけで、サンプリング判断上の親としては扱われない。

なので、SQSトリガーのLambdaは常にルートスパン扱いになり、traceidratio がそのまま適用される。

数字で見る

IoT機器100台が1分に1回SQS経由でイベントを送ってくる想定だと、

100台 × 1回/分 = 100イベント/分
100イベント ÷ 10件バッチ = 10 Lambda実行/分
10 × 60 × 24 = 14,400 Lambda実行/日

これに対して現状の設定値 OTEL_TRACES_SAMPLER_ARG: "0.3" を当てはめると、

14,400 × 0.30 = 4,320 trace/日

これはさすがに多い。エラーでも遅延でもない正常系のトレースがこの量取れても、コストが増えるだけでノイズも増える。1〜5%くらいから始めて、障害調査の時だけ一時的に上げる運用のほうが現実的そう。

一方、IoTモノの登録APIみたいに1モノにつき1回しか呼ばれない低頻度の処理なら、初期は100%取得しても影響は小さい。安定運用に入ってから20〜50%くらいに絞る、で十分そう。

結論

parentbased_traceidratio を使うときは、「親のサンプリング判断がどこから来るか」をトリガーごとに分けて考えないといけない。

  • API Gateway経由: 呼び出し元がtraceparentを送ってきて、それをLambda側が実際に抽出してる場合のみ継承。何もなければ結局ルート扱い
  • SQS経由: 親コンテキストは渡らない。常にルート扱いで traceidratio が直接効く

正常系だけをエラー時や遅延時と別に扱いたいなら、これはヘッドサンプリングの範囲では無理で、ADOT Collectorを挟んでtail samplingにする必要がある。Lambda単体では、生成時点で即サンプリング判断が決まってしまうので後から選別できない。実機でtail sampling構成までは試せてないので、そこは未確認のまま。