Step FunctionsのThrottlingExceptionをエクスポネンシャルバックオフで制御する

# はじめに

AWS の Step Functions と Glue Job を使用した開発中に発生した「ThrottlingException」をどのように解決したか共有します


# そもそもThrottling とは

「Throttling」とは、通信やデータ処理において、ある機能やシステムが使用可能な帯域幅やリクエスト数を制限することを指します。

# ThrottlingException とは

「ThrottlingException」は、AWS API を呼び出すときに、API へのリクエストが制限されたことを示すエラーです。
一度に多くのリクエストが送信されると、サービスのインフラに大きな負荷がかかり、システムの安定性が低下する可能性があります。そのような状況を避けるために、リクエストを受け入れず「ThrottlingException」を返すことがあります。

# エクスポネンシャルバックオフとは

エクスポネンシャルバックオフは、リトライの間隔を指数関数的に増加させることで、エラーが発生したときのリソースの競合を緩和する手法です。これにより、サービスの制限を超えるリクエストのリトライを制御できます。  
今回はこれを使用して、「ThrottlingException」が発生した場合でもAPIコールを再試行するようにしました。

# エクスポネンシャルバックオフを実装せずに 「ThrottlingException」を起こす

以下はエクスポネンシャルバックオフを実装せずに 「ThrottlingException」を起こす Step Functions コードです。
21個のGlue Jobを呼び出しています。

```json
{
  "Comment": "Parallel Execution of 21 GLUE Jobs",
  "StartAt": "ParallelExecution",
  "States": {
    "ParallelExecution": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "GlueJob1",
          "States": {
            "GlueJob1": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        },
        {
          "StartAt": "GlueJob2",
          "States": {
            "GlueJob2": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        },
        {
          "StartAt": "GlueJob3",
          "States": {
            "GlueJob3": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        },
        //省略
        {
          "StartAt": "GlueJob21",
          "States": {
            "GlueJob21": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        }
     ],
      "End": true
    }
  }
}
```
### 実行結果

![スクリーンショット 2023-04-19 21.33.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849053/5cc48a57-9600-6839-0455-079e69190852.png)

![スクリーンショット 2023-04-19 21.34.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849053/5135542b-824b-668b-1a7a-c3677027e96e.png)




# エクスポネンシャルバックオフの実装して 「ThrottlingException」を制御する

先ほどのコードをエラー発生時に エクスポネンシャルバックオフによるリトライを実行するようにコードを書き換えます。
リトライポリシーにおいて、 以下の三つを定義します。

- MaxAttempts  
  → 最大リトライ回数

- IntervalSecond  
  → リトライ間隔

- BackoffRate  
  → リトライ間隔を決定するためのバックオフレート  
    このレートは、前のリトライの間隔に乗算され、次のリトライの間隔を計算するために使用されます。  
    例えば、IntervalSeconds が 2 秒、BackoffRate が 2.0 の場合、  
    1 回目のリトライの間隔は 2 秒、  
    2 回目のリトライの間隔は 4 秒、  
    3 回目のリトライの間隔は 8 秒  
    という具合でリトライが実行されます。

```diff_json
{
  "Comment": "Parallel Execution of 21 GLUE Jobs",
  "StartAt": "ParallelExecution",
  "States": {
    "ParallelExecution": {
      "Type": "Parallel",
+     "Retry": [
+       {
+         "ErrorEquals": ["RateLimitExceeded"],
+         "MaxAttempts": 3,
+         "IntervalSeconds": 2,
+         "BackoffRate": 2.0
+       }
+     ],
      "Branches": [
        {
          "StartAt": "GlueJob1",
          "States": {
            "GlueJob1": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        },
        {
          "StartAt": "GlueJob2",
          "States": {
            "GlueJob2": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        },
        //省略
        {
          "StartAt": "GlueJob21",
          "States": {
            "GlueJob21": {
              "Type": "Task",
              "Resource": "arn:aws:states:::glue:startJobRun.sync",
              "Parameters": {
                "JobName": "yourjob"
              },
              "End": true
            }
          }
        }
     ],
      "End": true
    }
  }
}

```
### 実行結果
実行が正常終了しました
![スクリーンショット 2023-04-19 21.35.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849053/62deaf45-6a1e-6bf5-e8d2-fab4b2696189.png)



# エクスポネンシャルバックオフを適用することによるメリット

## システムの安定性向上

エクスポネンシャルバックオフによるリトライ制御によって、リクエストの競合が緩和されるため、各サービスが適切なリソースを確保でき、システム全体が安定して動作するようになります。

## パフォーマンスやコストへの影響

リトライ回数が抑制されるため、不要なリクエストやリソースの使用が削減され、コスト効率が向上します。ただし、リトライ間隔が長くなることでタスクの完了に時間がかかることもあり、パフォーマンスとコストのバランスを適切に調整することが重要です。

# まとめ
エクスポネンシャルバックオフの概要と実装方法について説明しました。
実装自体は簡単であるためパフォーマンスとコストのバランスをどうするのかというのがメインで考えることになるのかなと思います。
よくよく調べるとAWSを使用した開発であれば知っていて当たり前くらいの内容のものらしく、知らなかったことを恥じると同時に知れてよかったです。