【AWS】.syncを使わずにStep FunctionsからGlueJobを実行してみた【性能改善】

# 背景
Step Functionsで「StartJobRun.sync」を用いてGlueJobの実行をする場合、
ジョブの完了からStep Functions側でその完了を確認するまで、最大5分ほどかかる場合があります。（AWSサポートで確認済み：2022/7/23）

今回は「StartJobRun.sync」を使用しないことで、この遅延の短縮を試みました。

# StartJobRun.syncを使用しないステートマシンの作成（改善案）
改善案として、「StartJobRun」でジョブを実行した後に「GetJobRun」APIを定期実行し、
ジョブのステータスチェックを実施するステートマシンを作成しました。
今回は10秒おきにジョブのステータスをチェックする設定としています。

定義は以下の通りです。

・ワークフロー図
![stepfunctions_graph2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728075/2b164fb2-82a6-c68b-1ef8-a936b2a4157a.png)
・定義
```JSON:StartJobRun.syncを使用しないステートマシンの定義
{
  "StartAt": "glue-task1",
  "States": {
    "glue-task1": {
      "Next": "Wait",
      "Retry": [
        {
          "ErrorEquals": [
            "States.TaskFailed"
          ],
          "IntervalSeconds": 5,
          "MaxAttempts": 3
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "task-failed"
        }
      ],
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "作成したGlueJobの名前"
      }
    },
    "Wait": {
      "Type": "Wait",
      "Seconds": ここでステータスチェックの頻度を設定[秒]（今回は10）,
      "Next": "GetJobRun"
    },
    "GetJobRun": {
      "Next": "Job Complete?",
      "Retry": [
        {
          "ErrorEquals": [
            "States.TaskFailed"
          ],
          "IntervalSeconds": 5,
          "MaxAttempts": 5
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "task-failed"
        }
      ],
      "Type": "Task",
      "ResultPath": "$.GetJobRun",
      "Resource": "arn:aws:states:::aws-sdk:glue:getJobRun",
      "Parameters": {
        "JobName": "作成したGlueJobの名前",
        "RunId.$": "$.JobRunId"
      }
    },
    "Job Complete?": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.GetJobRun.JobRun.JobRunState",
          "StringEquals": "SUCCEEDED",
          "Next": "complete"
        },
        {
          "Variable": "$.GetJobRun.JobRun.JobRunState",
          "StringEquals": "STARTING",
          "Next": "Wait"
        },
        {
          "Variable": "$.GetJobRun.JobRun.JobRunState",
          "StringEquals": "RUNNING",
          "Next": "Wait"
        }
      ],
      "Default": "task-failed"
    },
    "task-failed": {
      "Type": "Fail",
      "Error": "Task Failed",
      "Cause": "Task Failed"
    },
    "complete": {
      "Type": "Pass",
      "End": true
    }
  },
  "TimeoutSeconds": 600
}
```
# StartJobRun.syncを使用したステートマシンの作成
比較対象として、構成自体は変えずにGlueJobの実行に「StartJobRun.sync」を使用したものを作成しました。

・ワークフロー図
![stepfunctions_graph1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728075/623dc112-4c4e-64ea-a79d-637b3975caa7.png)
・定義
```JSON:StartJobRun.syncを使用したステートマシンの定義
{
  "StartAt": "glue-task1",
  "States": {
    "glue-task1": {
      "Next": "complete",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "task-failed"
        }
      ],
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun.sync",
      "Parameters": {
        "JobName": "作成したGlueJobの名前"
      }
    },
    "task-failed": {
      "Type": "Fail",
      "Error": "Task Failed",
      "Cause": "Task Failed"
    },
    "complete": {
      "Type": "Pass",
      "End": true
    }
  },
  "TimeoutSeconds": 600
}
```
# 検証方法

上記2つのステートマシンから同じGlueJobを実行し、
Glueのコンソール上で確認できるジョブの終了時刻と、Step Functionsの次のステップ「complete」の開始時刻の差を計算して比較しました。

実行するGlueJobはPython Shellタイプで作成し、60秒間待機するだけのコードをScriptに記載しました。
```py:Scriptに記載したコード
import sys
import time
time.sleep(60)
```
# 結果
※2022/08/22実施
|  | .syncを使ったステートマシンの遅延 | .syncを使っていないステートマシンの遅延 |
|:-:|:-:|:-:|
| 1回目  | 00分37秒  | 00分05秒  |
| 2回目  | 00分40秒  | 00分05秒  |
| 3回目  | 00分47秒  | 00分06秒  |
| 4回目  | 00分53秒  | 00分06秒  |
| 5回目  | 00分23秒  | 00分01秒  |
| 6回目  | 00分53秒  | 00分05秒  |
| 7回目  | 00分52秒  | 00分06秒  |
| 8回目  | 00分37秒  | 00分05秒  |
| 9回目  | 00分29秒  | 00分09秒  |
| 10回目  | 00分37秒  | 00分10秒  |
| **平均**  | **00分41秒**  | **00分06秒**  |
# 考察
今回の計測では平均して約35秒の遅延の短縮が確認できました。

.syncを使っていないステートマシンでは最大10秒の遅延が見られましたが、
これはステータスチェックの頻度の設定値以内であり、想定される遅延時間内に収まっています。

GlueJobの実行時間を今回は60秒に固定しているため、他の実行時間では遅延の短縮効果に変化があると考えられますが、許容できる最大の遅延時間をステータスチェックの頻度に設定することで、許容以上の遅延の発生については防止出来ると思います。

また一つのステートマシンで複数回Jobを実行する場合では大幅な時間短縮が見込めそうです。

# 改善案のデメリット
今回の改善案のデメリットとしては、Step Functions上の実行イベント履歴が見にくくなることが挙げられます。

ジョブの実行に時間がかかる分だけステータスチェックのイベント履歴が増えるので、
運用保守で実行イベント履歴を使用している場合は、探したいログが埋もれてしまう原因になってしまいそうです。。。

※10秒ごとのステータスチェックでは約1分30秒の実行時間で90のイベントが発生
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728075/159bd396-a3d5-2fc3-bccf-418af47efd0e.png)

# 最後に
今回はGlueJobとの連携時の遅延について検証を行いましたが、.syncは他にもECSなどのサービスで使用することが出来るようです。

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-supported-services.html

.syncを使用しているステートマシンで処理時間を短縮したい場合には、今回紹介した方法を試してみてはいかがでしょうか。