AWS Transfer Familyのマネージドワークフローでスロットリング制限を確認してみた

2022.06.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

AWS Transfer Family には、マネージドワークフローという機能があり、以前簡単に機能をご紹介させて頂きました。

このマネージドワークフロー、実は制限事項がいくつかあります。

その中のひとつに

ワークフローごとの新しい実行リフィルレートは 1 秒あたり 1 です。

という記述があり、これが何なのかを今回は掘り下げてみました。

先にまとめ

いくつかのサービスクォーターのキーワードと動作確認の結果から、トークンバケットアルゴリズムでスロットリング制御されていることがわかりました。

AWS Transfer Family endpoints and quotas - AWS General Reference

  • Maximum number of new executions allowed per workflow at one time.
  • Each supported Region: 100

ワークフローが一度に許可される新規実行の最大数が100とされています。(調整不可能)

  • The new executions refill rate per workflow per second
  • Each supported Region: 1

そして、1秒あたりのワークフローごとの新しい実行のrefill rate(補充率)が1とされています。

スロットリング制限を超えた場合、Transfer Family はワークフローオペレーションをキューに追加しません。

ワークフローが1回実行されるごとに、トークンが1つ消費され、1秒ごとに補充されます。
また、トークンが不足している際は上記制限事項から、ワークフロー自体が実行されません。

検証

トークンバケットアルゴリズム自体はAWSでもAPI Gatewayのスロットリングの仕組みやEC2 APIなど各所で採用されていますのでそちらを参照ください。

以下では実際に動作検証した結果を記します。

方法

以下の記事でJMeterを使って、Transfer Familyへ一括送信を行う方法をご紹介しました。

今回は、この方法を使って特定の短期間に集中的にワークフロートリガーを発生させます。
ワークフローはシンプルなログ出力するだけのLambda関数を実行させるものを使います。

スレッド数と間隔

JMeterでスレッド数と実行時間を指定します。

この記事では以下のパターンを試しました。

  • 100スレッド/100秒間
  • 200スレッド/100秒間
  • 200スレッド/50秒間

実行後に、CloudWatch Logs の Transfer Family サーバーログと Lambda関数の実行ログを確認し、成功した数と失敗した数を集計します。
参考までに、マネージドワークフローが正常に動作するとCloudWatch LogsのTransfer Familyサーバーログへ以下のような内容が出力されます。

{
    "type": "ExecutionStarted",
    "details": {
        "input": {
            "initialFileLocation": {
                "backingStore": "S3",
                "bucket": "hoge0601trasnfer",
                "key": "user1/hoge1.txt",
                "etag": "f28339695e13a0073567e880ac43ddda"
            }
        }
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "311ca4e5-496e-4699-8d29-b6d79c3ef501",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "abd02ad44323e1cf"
    }
}
{
    "type": "StepStarted",
    "details": {
        "input": {
            "fileLocation": {
                "backingStore": "S3",
                "bucket": "hoge0601trasnfer",
                "key": "user1/hoge1.txt",
                "etag": "f28339695e13a0073567e880ac43ddda"
            }
        },
        "stepType": "CUSTOM",
        "stepName": "hoge-logging"
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "311ca4e5-496e-4699-8d29-b6d79c3ef501",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "abd02ad44323e1cf"
    }
}
{
    "type": "CustomStepInvoked",
    "details": {
        "output": {
            "token": "M2MxOTc3OTEtMzVlZC00YzNkLTlkNzMtMGU2NDRlY2MzMjZj"
        },
        "stepType": "CUSTOM",
        "stepName": "hoge-logging"
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "311ca4e5-496e-4699-8d29-b6d79c3ef501",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "abd02ad44323e1cf"
    }
}
{
    "type": "StepCompleted",
    "details": {
        "output": {
            "token": "M2MxOTc3OTEtMzVlZC00YzNkLTlkNzMtMGU2NDRlY2MzMjZj"
        },
        "stepType": "CUSTOM",
        "stepName": "hoge-logging"
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "311ca4e5-496e-4699-8d29-b6d79c3ef501",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "abd02ad44323e1cf"
    }
}
{
    "type": "ExecutionCompleted",
    "details": {},
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "311ca4e5-496e-4699-8d29-b6d79c3ef501",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "abd02ad44323e1cf"
    }
}

実行結果

100スレッド/100秒間

100スレッドすべて実行されました。

こちらはトークンバケットへデフォルトで100プールされているので、1秒間にバーストしたとしても100スレッドまでは問題なく処理される理屈なので期待どおりの動作ですね。

200スレッド/100秒間

200スレッドすべて実行されました。

こちらはトークンバケットの100トークンを使い果たしたので100スレッドのみ成功して100スレッドは失敗しそうですが、100秒間かけて均等に実行されるので、スタートの100トークン+100秒間で補充される100トークンで、200トークンまで利用可能になります。

タイミングによっては数スレッドは失敗する可能性はあるかなと考えていたのですが、全て成功しました。

200スレッド/50秒間

148スレッド実行され、52スレッドはスロットリング制限でスキップされました。

これは先程の計算から考えると、スタートの100+50秒間で補充される50で150トークン利用可能になる計算になりますので、148スレッド実行され52スレッドがスキップされたのは妥当な感じがしますね。

トークンバケットアルゴリズムを採用しており、サービスクォーターに記載のとおり100トークンで開始、秒間1トークン補充されていそうです。

スロットリング制限を超えた時の挙動

スロットリング制限を超えた時の挙動についてもまとめておきます。
まず、一番重要な点として、前述の検証結果のとおりワークフローの実行が行われません。
先程の例ではSFTPクライアントで200ファイルPUTしたのですが、PUTをトリガーとするLambdaは148回のみ実行されました。
ちなみに、S3バケットへのファイルアップロード自体は200ファイル正常に終了しています。

CloudWatch Logsへ出力されたログ内容を確認してみましょう。
スロットリング制限によりスキップされた場合は以下のログが出力されます。

{
    "type": "ExecutionThrottled",
    "details": {
        "input": {
            "initialFileLocation": {
                "backingStore": "S3",
                "bucket": "hoge0601trasnfer",
                "key": "user1/hoge1.txt",
                "etag": "f28339695e13a0073567e880ac43ddda"
            }
        }
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "0c5b03fd-3da0-4386-b9d2-a1dd4b109523",
    "transferDetails": {
        "serverId": "s-854f6c60a42643588",
        "username": "user1",
        "sessionId": "4a1d7b6e4b4a0321"
    }
}

ちなみに、ワークフロー実行結果の集計は、CloudWatch Logs Insightsを使って集計を行いました。
以下はスロットリング制限に引っかかった回数を集計しています。

以下は正常にワークフローを実行出来た回数を集計しています。

スロットリングにより実行されなかった場合にどうするか

まず、どうやらマネージドワークフローとしてデッドレターキューみたいな仕組みはなさそうなのでスロットリング制限の場合に再試行する仕組みは用意されていないということがわかりました。

では、まずはどうやってスロットリング制限に引っかかったかを検知出来るかを調べてみます。

CloudWatchメトリクス

マネージドワークフローに関するメトリクスがいくつか存在しています。
これらが利用出来るか調べてみます。

  • OnUploadExecutionsStarted
    • サーバー上で開始されたワークフロー実行の総数
  • OnUploadExecutionsSuccess
    • サーバー上で成功したワークフロー実行の総数
  • OnUploadExecutionsFailed
    • サーバー上で失敗したワークフロー実行の総数

うまく失敗が検知出来そうなのですが、結論を先に言うとスロットリング制限ではワークフロー実行が失敗するわけではなく、そもそもワークフローが実行されていないので、使えませんでした。

160スレッド/50秒でSFTPのPUT操作を行った場合のワークフローメトリクスを調べてみましょう。
150スレッド処理出来るはずなので、±10程度がスロットリング制限で実行されないはずです。

この時のメトリクスは以下となります。

実行総数も成功総数も150でした。
そして失敗総数は0でした。残念。

ちなみに、この失敗総数ですが、ワークフローに統合されたLambdaがエラー終了したりタイムアウトが起きると失敗として扱われます。
失敗時は以下のように明示的にエラーで終了したというログも出力されます。

{
    "type": "StepErrored",
    "details": {
        "errorType": "TIMEOUT",
        "stepType": "CUSTOM",
        "stepName": "hoge-logging"
    },
    "workflowId": "w-5e7112ad12dfeb5e2",
    "executionId": "ca1dc04a-319f-48bc-b618-a27181346724",
    "transferDetails": {
        "serverId": "s-8f8983935eec41118",
        "username": "user2",
        "sessionId": "224f42427d1f514f"
    }
}

さいごに

本日はTransfer Familyのマネージドワークフローのスロットリング制限を確認・検証してみました。
どういう仕様でどういう挙動になるのがよく理解出来ました。

スロットリング制限を検知する方法としては、メトリクスフィルターなどを使ってログから拾うのが考えられそうです。
このあたりも次回以降やってみたいと思います。