サーバーレスアプリケーションでエラーに対処する (AWS Step Functions + AWS Lambda)

Lambda 関数は、以下のような場合、処理に失敗することがあります。
・処理されない例外
・タイムアウトよりも長い時間実行された場合
・メモリ不足が発生した場合
このような場合、AWS Step Functions を使用して、エラーを適切に処理するサーバーレスワークフローを設計し、実行します。

---

###1.API に模擬コールを行う Lambda 関数を作成する###
この Lambda 関数では、イベントパラメータで入力として指定されたエラーコードに応じて例外を発生させ、架空の API からの応答をシミュレートします。

Lambdaを開き、「一から作成」で、Lambda 関数を以下のように設定し、「関数の作成」をクリック。
・名前：「適当な名前」
・ランタイム：「Python 3.6」
・ロール：「lambda_basic_execution」
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/229f8bef-0b6b-8cd2-3641-5566d0b7ba55.png)

[関数コード]を以下のように編集し、「保存」してください。
ここでは、API の模擬応答を実行する簡単な Lambda 関数の作成です。


```PHP:
class TooManyRequestsException(Exception): pass
class ServerUnavailableException(Exception): pass
class UnknownException(Exception): pass

def lambda_handler(event, context):
    statuscode = event["statuscode"]    
    if statuscode == "429":
        raise TooManyRequestsException('429 Too Many Requests')
    elif statuscode == "503":
        raise ServerUnavailableException('503 Server Unavailable')
    elif statuscode == "200":
        return '200 OK'
    else:
        raise UnknownException('Unknown error')
```

画面右上の ARN を書き留めておいてください。


###2.IAM ロールを作成する###

[このロールを使用するサービス]で「Step Functions」を選択してIAM ロールを作成しておいてください。


###3.Step Functions のステートマシンを作成する###

Step Functions を開き、「一から作成」します。
[名前]:適当な名前
[ステートマシンの定義]:以下のコードを入力します。
なお、Resource 部分は自分のものにかえてください。

```
{
  "Comment": "An example of using retry and catch to handle API responses",
  "StartAt": "Call API",
  "States": {
    "Call API": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
      "Next" : "OK",
      "Comment": "Catch a 429 (Too many requests) API exception, and resubmit the failed request in a rate-limiting fashion.",
      "Retry" : [ {
        "ErrorEquals": [ "TooManyRequestsException" ],
        "IntervalSeconds": 1,
        "MaxAttempts": 2
      } ],
      "Catch": [ 
        {
          "ErrorEquals": ["TooManyRequestsException"],
          "Next": "Wait and Try Later"
        }, {
          "ErrorEquals": ["ServerUnavailableException"],
          "Next": "Server Unavailable"
        }, {
          "ErrorEquals": ["States.ALL"],
          "Next": "Catch All"
        }
      ]
    },
    "Wait and Try Later": {
      "Type": "Wait",
      "Seconds" : 1,
      "Next" : "Change to 200"
    },
    "Server Unavailable": {
      "Type": "Fail",
      "Error":"ServerUnavailable",
      "Cause": "The server is currently unable to handle the request."
    },
    "Catch All": {
      "Type": "Fail",
      "Cause": "Unknown error!",
      "Error": "An error of unknown type occurred"
    },
    "Change to 200": {
      "Type": "Pass",
      "Result": {"statuscode" :"200"} ,
      "Next": "Call API"
    },
    "OK": {
      "Type": "Pass",
      "Result": "The request has succeeded.",
      "End": true
    }
  }
}
```

「既存のIAMロールを作成する」を選択し、2.で作成したIAMロールを紐づけます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/513a132b-73cc-60cd-edee-8493ffdf7c4c.png)


###4.エラー処理ワークフローをテストする###

「実行の開始」をする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/40f53338-42f6-8a8a-a46e-8f50cc529e88.png)

「新しい実行」 画面でユーザーが API の部分を担当し、模擬 API によって返してもらいたいエラーコードを指定します。
以下のコードを入力し、「実行の開始」をクリックします。

```
{
    "statuscode": "200"
}
```

「入力」 をクリックし、↑ が入力されていることを確認します。
「出力」 をクリックすると、ステータスコード 200 がワークフローによって API コールの成功として解釈されたことを確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/7b685723-a8e9-5cf2-5468-9c71303403d8.png)

[ビジュアルワークフロー] で、各実行の実行パスを確認できます。
"Call API" をクリックすると [入力] フィールドと [出力] フィールドが展開されます。
この状態によって、指定した入力を使用して模擬 API Lambda 関数が正常に呼び出され、その Lambda 関数の出力 "200 OK" が取得されたことを確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/a216e916-8ff5-c400-3b76-9e45c662223f.png)

"OK" をクリックします。
[ステップの詳細] で、前のステップの出力 (Call API) が、このステップへの入力として渡されたことを確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/3521a4a5-ec47-d9b9-2837-a663ac4d143f.png)


###5.ステートマシンの実行を検証する###

今回は以下の入力を指定して 「実行の開始」 をクリック。

```
{
    "statuscode": "503"
}
```

この実行は失敗すると想定しているため、驚かないでください。 以下のプロセスが実行されたことがわかります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/2449d7cc-31a9-6e2c-6053-21b50ef7e56c.png)

429 例外のシミュレーションを行います。
「実行の開始」をクリックし、以下を入力します。

```
{
    "statuscode": "429"
}
```

ワークフローの再試行動作を確認していきましょう。

[実行イベント履歴] でFunctions によって MockAPILambda 関数がもう 2 回呼び出されていることを確認します。
この呼び出しは両方とも失敗しています。この時点では、ワークフローは、API が一時的に応答していないだけと考えて、Wait and Try Later 状態 (右側の画像に表示) に遷移します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/a7c85902-4827-8430-3a70-58cbc9781f4b.png)

次に、Wait 状態によって強制的に応答コードが 200 に変更されるため、ワークフローは実行を正常に完了できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/136323bb-a570-a5ff-ed10-9fb56419eadd.png)

今度は、ステートマシンによって処理されないランダムな API 応答を指定してみましょう。

```
{
    "statuscode": "999"
}
```

失敗ともちろんなる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/280929/5ae577b3-e7bf-3be2-66fb-2a7ea54011ef.png)