API Gateway 統合タイムアウト対策


こんにちは、松田です。

突然ですが、皆さんはこんな場面に遭遇したことはないでしょうか。
API Gatewayを経由してLambdaを実行した際にLambdaの処理時間が長くAPI Gatewayタイムアウトしてしまう。
今回は上記の対策として、Step Functionsを使ってみました。

用語説明



API Gatewayとは
正式名称を「Amazon API Gateway」という
APIの構築が簡単になるサービス。
また、IAMやcognitoを用いることでアクセスの制限も行える。

Lambdaとは
正式名称を「AWS Lambda」という
サーバーレスでコードを実行できるサービス

Step Functionsとは
正式名称を「AWS Step Functions」という
Lambdaの関数から別のLambdaの呼び出しが簡単になるサービス

対策方法


まずはStep Functionsを用いなかった場合


タイムアウトをさせる関数↓

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        body: "great success!",
    };
    
    console.log("停止");
    sleep(30000); //30秒待つ
    console.log("再開");

    function sleep(time) {
        const d1 = new Date();
        while (true) {
            const d2 = new Date();
            if (d2 - d1 > time) {
                return;
            }
        }
    }

    return response;
};


タイムアウトのリクエスト↓


API Gatewayの統合タイムアウト(29000ms)を超えたため
下記のエラーが出ます。

"message": "Endpoint request timed out"




では、今回の本題「統合タイムアウトの対策」です。


タイムアウトをした記述は
リクエスト→API Gateway→Lambda1(行いたい処理)でしたが
タイムアウトの対策は
リクエスト→API Gateway→Lambda2(呼び出し用)→Step Functions→Lambda1(行いたい処理)の流れで行います。

以下、タイムアウト対策


Step Functions呼び出し関数↓

exports.handler = async (event) => {
    const AWS = require("aws-sdk");
    
    const stepFunctions = new AWS.StepFunctions({
        accessKeyId: '********************',
        secretAccessKey: '****************************************',
        region: 'us-east-1'
    });
    
    let errorMessage = "";
    let params = {
        "stateMachineArn": "arn:aws:states:us-east-1:************:stateMachine:timeoutStepFunction"
    };
    let statusParams = {
        "executionArn": ""
    };
    
    await stepFunctions.startExecution(params).promise().then(res => {
        console.log("successfully");
        console.log(res);
        statusParams.executionArn = res.executionArn;
    }).catch(error => {
        console.log("failed:" + error);
        console.log(error);
        errorMessage = error;
    });
    
    console.log(statusParams);
    
    if(errorMessage == ""){
        return stepFunctions.describeExecution(statusParams).promise().then(res => {
            console.log(`Your statemachine executed successfully`);
            const response = {
                statusCode: 200,
                body: res,
            };
            return response;
        }).catch(error => {
            console.log("failed:" + error);
            const response = {
                statusCode: 500,
                body: error,
            };
            return response;
        });
    }
    
    return errorMessage;
};


正常な動作の場合のレスポンス

{
  "statusCode": 200,
  "body": {
    "executionArn": "arn:aws:states:us-east-1:************:execution:timeoutStepFunction:14a32f58-ab32-45d0-a631-c26e135fdf4f",
    "stateMachineArn": "arn:aws:states:us-east-1:************:stateMachine:timeoutStepFunction",
    "name": "14a32f58-ab32-45d0-a631-c26e135fdf4f",
    "status": "RUNNING",
    "startDate": "2020-10-06T07:31:17.855Z",
    "input": "{}"
  }
}


実際に使用する場合は
statusがRUNNING(実行中)の場合はSUCCEEDEDが返ってくるまで再度リクエストを出す。
SUCCEEDED(完了)の場合は次の動作に移るといった使用方法になると思います。

悩んだ点

  1. AccessDeniedExceptionの原因が分からない
    調べているとIAMロールの権限がないといった情報が多く出てきますが、
    私の場合はアクセスキーとシークレットアクセスキーを設定していないことが原因でした。

  2. StepFunctionを呼び出しているのにstatus(実行状態)が取得できない
    startExecutionではstatusは返せません。
    describeExecutionを使いましょう。
    公式ドキュメントに載っていたのでちゃんと見るようにしましょう・・・

docs.aws.amazon.com

最後に


統合タイムアウト対策いかがだったでしょうか?
以前のプロジェクトで使用したことがあったため記述してみました!
ここまでご覧いただきありがとうございました!
またどこかでお会いしましょう~

アプリケーション開発バナー

AWS相談会バナー