AWS
lambda

AWS Lambdaでストリームベースのイベントソースのリトライ回数を有限にしてDLQを使う

はじめに

AWS Lambdaのリトライの挙動は、「ストリームベースでないイベントソース」と「ストリームベースのイベントソース」で大きく異なります1

参考: エラー時の再試行

  • ストリームベースでないイベントソース
    • 非同期呼び出し
      • 2回のリトライを含め、合計3回実行される
    • 同期呼び出し
      • 呼び出し側にエラーが返る(リトライする場合は、呼び出し側が再度呼び出す)
  • ストリームベースのイベントソース
    • データの有効期限が切れるまで、無限にリトライ

各トリガがどちらのイベントソースに該当するかについては、基本的には、「ストリームベースのイベントソース」はAmazon KinesisストリームとDynamoDB ストリーム、「ストリーベースでないイベントソース」はそれ以外、だと覚えておけばよいかと思います。

ストリームベースのイベントソースのリトライを有限にしたい

さて、本題です。

KinesisストリームやDynamoDBストリームのLambdaハンドラで、エラーがあった場合、成功するか有効期限が切れるまで延々とリトライが繰り返されるわけですが、場合によっては、有限のリトライを行った後、デッドレターキューに詰めた上で、次のデータを処理してほしいケースがあるかもしれません。

そのための方法を考えてみました。

やり方は単純で、単体のLambdaの中でDynamoDBイベントのハンドリングと実処理を行なうのではなく、Lambdaを分割し、実処理を行うLambdaを非同期でキック(invoke)して実行させる、というものです。

なんのこっちゃ、ですが、図にすると簡単です。

Dynamo-Lambda.png

要するに、Lambdaを多段構成にしてしまうわけですね。2つのLambdaの役割は次のようになります。

  • dynamo-handler
    • DynamoDBストリームからイベントを受け取り、実処理を行なうLambda(dynamo-processor)を非同期で呼び出す
  • dynamo-processor
    • 実際のDynamoDBストリームイベントを処理する
    • 「ストリームベースでないイベントソース(非同期呼び出し)」としての扱いになるので、2回のリトライやデッドレターキューが利用可能になる

具体的なコードは次のようになります。

dynamo-handler

dynamo-handler
var aws = require('aws-sdk');

exports.handler = (event, context, callback) => {    
    var lambda = new aws.Lambda({apiVersion: '2014-11-11'});
    var params = {
        FunctionName: "dynamo-processor",
        InvokeArgs: JSON.stringify({ Records: event.Records }, null, ' ')
    };

    lambda.invokeAsync(params, (err, data) => {
        if(err) callback(null, 'error ' + JSON.stringify(err));
        else callback(null, 'success ' + JSON.stringify(data));
    });
};

dynamo-processor がイベントを処理できるようにするために、 event.RecordsInvokeArgs で渡してあげることです。

dynamo-processor

dynamo-processor
exports.handler = (event, context, callback) => {
    var records = event.Records;

    // 実際の処理を書く(略)

    callback(null, 'success');
};

実際の処理は適当に…。

DynamoDBやデッドレターキューの設定は省略します。

わざと常に失敗する dynamo-processor を作って試してみましたが、3回実行された後、Dead Letter Queueに入っていることが確認できました。

リトライの待機時間

リトライがどれくらいの間隔で行われるのかについてですが、AWSのドキュメントには、

再試行間には遅延があります

としか書かれていません (http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html)

実際にどれくらいになったのか、参考値として今回の実験結果を紹介します。

  • 1件目
    • 1回目 13:49:11
    • 2回目 13:50:17
    • 3回目 14:00:17
  • 2件目
    • 1回目 13:57:19
    • 2回目 13:58:17
    • 3回目 14:00:14

となりました。

非同期呼び出しにしたため、3回目の実行時刻で、1件目と2件目で追い越しが発生していますね。
試行回数が少なすぎてなんとも言えないですが、最初のリトライは大体1分後に行われる、のかもしれません。

おわりに

KinesisストリームやDynamoDBストリームをトリガにしたLambdaでのリトライ・DLQについてでした。

注意点として、このような変更を加えることで、実行順序は保証されなくなります。
したがって、アプリケーションが実行順序が保証されなくても問題ないような設計、実装になっているか、そもそも要件的に問題がないか、きちんと検討が必要です。
ご利用は計画的に…。


  1. リトライの挙動だけでなく、同時実行数などでも違いがあります。