Lambdaに再帰ループの停止機能が追加されたので試してみる

ノッパクン
ノッパクン

この間、岐阜県の上流の川に遊びにいったんですが、猛暑の中、川の水がキンキンに冷えていて最高でした!暑い夏も早く終わるいいですね・・・。どうも、ノッパクンです。

概要

Lambdaに再帰ループの停止機能が追加されたらしいので動作確認してみました。16回のループを検知すると自動的にLambdaが停止する(新たに起動しなくなる)そうです。

↓公式の案内はこちら
AWS Lambda で Lambda 関数の再帰ループの検出と停止が可能に

↓本記事の動作確認用ソースはGitHubにあげてありますので、自分でも試してみたいという方は参考にしてください。
lambda-loop-test

SQSとLambdaの再帰ループ動作確認

まずはじめに、SQSトリガーを設定したLambdaで再帰ループを起こしていきます。それでは動作確認していきましょ〜!

再帰ループLambdaの作成

SQSにメッセージを送信する単純なLambdaを作成します。起動したことが分かりやすいようにログを仕込んでおきます。

import * as sqs from "@aws-sdk/client-sqs";

exports.handler = async function () {
  const client = new sqs.SQSClient({});
  const command = new sqs.SendMessageCommand({
    QueueUrl: process.env.QUEUE_URL,
    MessageBody: "this is custom message!",
  });

  await client.send(command);

  console.log("★★★★★★★★★★ hello sqs");
};

続いて、Lambdaのインフラの定義と、SQSをトリガーにLambdaが起動するように設定します。↓これによりLambdaが再帰ループするようになりました。cdkのデプロイもすませておきます。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as sqs from "aws-cdk-lib/aws-sqs";
import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";

export class LambdaLoopTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const sampleSqs = new sqs.Queue(this, "SampleSqs", {});

    const sqsFunction = new lambda.Function(this, "SqsFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: "index.handler",
      code: lambda.Code.fromAsset("src/lambda/SqsFunction"),
      environment: {
        QUEUE_URL: sampleSqs.queueUrl,
      },
    });

    sampleSqs.grantSendMessages(sqsFunction);
    sqsFunction.addEventSource(new SqsEventSource(sampleSqs));
  }
}

Lambdaの起動とログ確認

デプロイが完了したら、LambdaのWebコンソール上からテスト起動します。

実行が完了してからCloudWatchのログを確認してみましたが、起動ログが16回出力されていて、以降はログが出力されない(再帰ループが停止した)事が確認できました!

再帰ループ停止後しばらくたつと、公式の案内にあるとおりAWS Health Dashboardに通知があがってきました。通知内容は「再帰ループを修正してください&意図的にループを許容する場合はサポートに問い合わせください」の旨が記載されたものでした。

ちなみに私の環境の場合は再帰ループを停止した旨のメールが送付されました。(おそらくデフォルトでメール通知)

S3とLambdaの再帰ループ動作確認

S3でも同様に再帰ループが停止されるのか気になったので確かめてみました。

再帰ループLambdaの作成

S3にテキストファイルをPutする単純なLambdaを作成します。

import * as s3 from "@aws-sdk/client-s3";

exports.handler = async function () {
  const client = new s3.S3Client({});
  const command = new s3.PutObjectCommand({
    Bucket: process.env.BUCKET,
    Key: "test.txt",
    Body: "test",
  });

  await client.send(command);

  console.log("★★★★★★★★★★ hello s3");
};

SQSと同様に再帰ループが起きるようにCDKを定義してデプロイします。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as sqs from "aws-cdk-lib/aws-sqs";
import * as s3 from "aws-cdk-lib/aws-s3";
import {
  SqsEventSource,
  S3EventSource,
} from "aws-cdk-lib/aws-lambda-event-sources";

export class LambdaLoopTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const sampleSqs = new sqs.Queue(this, "SampleSqs", {});

    const sqsFunction = new lambda.Function(this, "SqsFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: "index.handler",
      code: lambda.Code.fromAsset("src/lambda/SqsFunction"),
      environment: {
        QUEUE_URL: sampleSqs.queueUrl,
      },
    });

    sampleSqs.grantSendMessages(sqsFunction);
    sqsFunction.addEventSource(new SqsEventSource(sampleSqs));

    const sampleS3 = new s3.Bucket(this, "SampleS3");

    const s3Function = new lambda.Function(this, "S3Function", {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: "index.handler",
      code: lambda.Code.fromAsset("src/lambda/S3Function"),
      environment: {
        BUCKET: sampleS3.bucketName,
      },
    });

    sampleS3.grantPut(s3Function);
    s3Function.addEventSource(
      new S3EventSource(sampleS3, { events: [s3.EventType.OBJECT_CREATED_PUT] })
    );
  }
}

Lambdaの起動とログ確認

Lambdaを起動してみましたが、ログが16回以上出力されています・・・。というか再帰ループに陥っていてログが止まりません!

↓Lambdaの同時実行数を0に設定すると再帰ループが止まるとのことで、慌てて設定しましたが、結局86回も実行されてしまいました・・・。S3トリガーでは再帰ループの停止機能は動作しないようです。

公式の案内ではたしかにSQSとSNSについてしか書かれていませんでしたが、S3で再帰ループは回避できない旨の注意喚起があっても良いのに・・・。

停止機能は、サポート対象となるSDKバージョン以降であれば自動的に有効となります。また、現状で対応しているのはSQSやSNSとの連携のみのようです。→Lambda の再帰ループ検出

まとめ

  • SQSやSNSと連携したLambdaの再帰ループは、16回ループすると自動的に停止する。
  • 再帰ループの停止はサポート対象のSDKバージョン以降で有効となる。
  • 再帰ループが発生してしばらくするとAWS Health Dashboardとメールに通知があがる。
  • 再帰ループを意図的に起こす場合はサポートに問い合わせる。
  • S3と連携したLambdaでは再帰ループは回避できない。
ノッパクン
ノッパクン

近いうちにまた川遊びにいこうと思います。皆さんも川遊びに行かれる際は水難事故に十分注意して楽しんでくださいね!(最近はライフジャケット着用が常識だそうです)それでは失礼します!