Pathee engineering blog

世界をしなやかに変えるエンジニアたちのブログ

AWS Lambda(など)の失敗をうまく取り扱う

Calendar for Pathee | Advent Calendar 2021 - Qiita

Pathee Advent Calendar 2021 9日目の記事です。

STORECASTエンジニアのkeisei1092です。
Patheeにジョインし、バックエンドを本格的に触るようになってから間もなく丸2年となります。

STORECASTにおける非同期処理や定期実行処理は、Serverless Frameworkを用い、AWS Lambda、Step Functionsなどのリソースをデプロイして動いています。

Lambdaでの処理や内外との疎通において、失敗をうまく処理することは大切です。 今年までに対応を行ったケースを振り返ってみたいと思います。

Lambda内でのAPIエラーについて

Lambda内でAPIリクエストを行った際に、エラーが返ってきた場合、エラーの種類によって、以下のような対応をします。

  • 400系エラーの場合
    • リクエストに問題があるため、エラー内容を通知し、処理終了
  • 500系エラーの場合
    • サーバに問題があるため、リトライを行う
    • サーバ側が詰まっていることが考えられるため、間隔を開けてリトライを行う
      • retrying モジュールを使うとお手軽に実装できる

Step FunctionsのMapステート内部の失敗について

Step FunctionsのMapステートを使って、複数のタスクを順番に実行する際に、あるタスクがエラー終了すると、後続のタスクが処理されません。
これを防ぐために、Mapステート内のエラー終了する可能性のあるタスクにはCatchフィールドを、後続のステートとしてHandleExceptionステート(名前は何でも良い)を定義します。

失敗しても後続のタスクが実行されるようなMapステートの例を以下に示します。

SomeState:
  Type: Map
  InputPath: "$.some_ids.body"
  ItemsPath: "$"
  MaxConcurrency: 1
  Iterator:
    StartAt: SomeFunction
    States:
      SomeFunction:
        Type: Task
        Resource: arn:aws:states:::states:startExecution.sync
        Parameters:
          StateMachineArn:
            Ref: SomeFunction
          Input:
             AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ID.$: "$$.Execution.Id"
             some_id.$: "$"
         OutputPath: "$.Output"
         Catch:
           - ErrorEquals:
               - States.ALL
             Next: HandleException
         End: true
       HandleException:
         Type: Pass
         End: true
   End: true

ErrorEquals の中に例外の名前を指定すれば、特定の例外の場合にのみCatchすることも可能です。
Retryフィールドも併用すれば、再実行したい例外の種別と終了したい例外の種別で流れを分けることができます。

SQSからトリガーしたLambdaのエラーについて

SQSのメッセージ経由でLambdaを実行する際、SQSの以下の概念を理解しておくとエラー時の流れを追いやすいです。

  • メッセージ保持期間 (Message Retention Period)
    • キューに存在するメッセージが削除されるまでの期間
  • 累計受け取り回数 (Maximum Receives)
    • キューに存在するメッセージが削除されるまでの受け取り回数
  • デッドレターキュー (Dead Letter Queue)
    • 正常に処理できないメッセージを移動するキュー

SQSのメッセージ経由で実行されたLambdaがエラー終了した場合、そのメッセージがキューの指定したメッセージ保持期間以内かつ累計受け取り回数以内であれば、キューに戻ります。
キューに戻ったメッセージは、再度Lambdaに渡って実行されます。
メッセージ保持期間を超えたり、累計受け取り回数分失敗し続けたメッセージは、デッドレターキューに移動されます。

デッドレターキューには、届いたメッセージをSlackに通知するLambdaを連携させたキューを設定することで、失敗し続けたメッセージを開発者が検知する運用にしています。

終わりに

この辺の動きは見えにくく、概念を知らないと追うのが難しいですが、一度理解すれば以後運用しやすいです。
AWSのマニュアルで各サービスの概念や、処理の流れをよく理解しておき、今後別のサービスと疎通する場合にもしっかり開発時の考慮に組み込めるようにしていきたいです。