Lambdaタイムアウト時にX-Rayサブセグメントが記録されないことがあるので注意しよう
私が気づいたのはLambdaがタイムアウトした時でしたが、実際の条件は
「サブセグメントをクローズせずにLambdaが終了した場合」
です。
条件と注意点について、詳しく説明していきます。
今回のコード全体はこちらに載せています。
クローズを忘れてしまいサブセグメントが記録されないコード例
良い例と悪い例を紹介します。
良い例
export const handler = async (event: { sleepTime: number }) => { const segment = AWSXRay.getSegment(); // 1000msのセグメントを取る const subSegment = segment?.addNewSubsegment("close"); // ~~~何かしらの処理~~~ // subSegment?.close(); return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda!", }), }; }
悪い例
export const handler = async (event: { sleepTime: number }) => { const segment = AWSXRay.getSegment(); const notClosedSegment = segment?.addNewSubsegment("notClosed"); return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda!", }), }; }
こちらのコードでは、segmentのcloseをしていないため、記録されません。
タイムアウトでサブセグメントが記録されないコード例
前例のコードのように、X-Rayを利用している方で close
を書き忘れることはあまりないと思いますが、Lambdaのタイムアウトでも同じ事象が起こります。
以下のようなコードを書き、Lambdaをタイムアウトさせてみます。
Lambda自体のタイムアウトは10秒に設定しました。
import * as AWSXRay from "aws-xray-sdk-core"; export const handler = async (event: { sleepTime: number }) => { const segment = AWSXRay.getSegment(); // 1000msのセグメントを取る const initialSegment = segment?.addNewSubsegment("init"); await new Promise((resolve) => setTimeout(resolve, 1000)); initialSegment?.close(); // eventの指定秒数のセグメントを取る const sleepSegment = segment?.addNewSubsegment("sleep"); if (event.sleepTime > 0) await new Promise((resolve) => setTimeout(resolve, event.sleepTime)); sleepSegment?.close(); return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda!", }), }; };
タイムアウトしない場合の結果
event
{ "sleepTime": 2000 }
実行結果
タイムアウトした場合の結果
event
{ "sleepTime": 11000 }
実行結果
取ろうとしていたサブセグメントが取れていないことが確認できます。
起こりうるケース: 外部APIがタイムアウト
実際に起こりうるケースとして、外部APIへの接続でタイムアウトを設定しておらず、Lambdaのタイムアウトに陥ってしまいサブセグメントが取れないことがあります。
AWSXRay.captureHTTPsGlobal
を利用すると、外部APIへの接続のセグメントを自動で取得してくれるようになります。
import * as AWSXRay from "aws-xray-sdk-core"; import * as http from "http"; import * as https from "https"; import axios from "axios"; export const handler = async (event: { sleepTime: number }) => { AWSXRay.captureHTTPsGlobal(http); AWSXRay.captureHTTPsGlobal(https); const reponse = await axios.get("https://ここがタイムアウトする.com"); return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda!", }), }; };
この場合、axiosのタイムアウトを設定していないことが問題なので、適切にタイムアウトを設定しましょう。