Amazon Connect をフロントとしたシステムで外部 API が意図せず複数回実行されてしまう場合の確認ポイント

記事タイトルとURLをコピーする

こんにちは、アプリケーションサービス部 ディベロップメントサービス2課の森田です。 Amazon Connect をフロントとし、AWS Lambda 経由で外部 API を叩くような案件で、意図せずに外部 API が複数回叩かれてしまったことがありました。
本記事では、その際に考慮したポイントについて書きたいと思います。

実際の案件で発生した事象なので、参考にしていただけると幸いです!

結論

Amazon Connect と AWS Lambda を組み合わせてシステム開発する際は、Amazon Connect と AWS Lambda のタイムアウト値に注意しましょう!

AWS Lambda のタイムアウトを起因に複数回のリトライをさせたくないのであれば、Amazon Connect と AWS Lambda のタイムアウト値を同じにしましょう。

逆に、 Amazon Connect 側のタイムアウト値 > AWS Lambda のタイムアウト値 とすれば複数回のリトライ処理を実現できます。

背景

今回扱ったシステムは、予約系の外部 API でした。
具体的のシステムの流れは以下になります。

  1. Amazon Connect で顧客からので電話を受電する。

  2. 顧客にガイダンス(新規予約される方は「1」を、予約を変更される方は「2」を〜 というやつです)に沿って予約情報を入力してもらう。

  3. Amazon Connect から AWS Lambda を呼び出し、顧客が入力した予約情報を AWS Lambda に受け渡す。

  4. AWS Lambda 内で予約情報を外部 API に送信する。

  5. 予約完了。

この予約システムで意図せず外部 API が複数回叩かれてしまったので、
外部 API 側で同じ人が同じ時間に複数回予約したこととなり、ダブルブッキングエラーになってしまいました。

それでは、複数回実行されてしまうプロセスを見ていきましょう。

1-1. システム概要

下図のように、「Amazon Connect から AWS Lambda を呼び出し、AWS Lambda 内で外部 API を呼び出す」というシステムがあります。

1-2. AWS Lambda でタイムアウトエラー

外部 API を叩いた際に、外部 API からのレスポンスが遅いなどして、AWS Lambda がタイムアウトエラーになりました。

AWS Lambda 自体はタイムアウトしていますが、外部 API を叩いた後にタイムアウトしていますので、外部 API への処理は実行されています。

1-3. AWS Lambda の再実行

この時、
Amazon Connect の 「AWS Lambda 関数を呼び出す」ブロックのタイムアウト値 > AWS Lambda のタイムアウト値
だった場合、Amazon Connect のタイムアウト値いっぱいまで最大3回 AWS Lambda を再実行します。

例)
Amazon Connect の 「AWS Lambda 関数を呼び出す」ブロックのタイムアウト値: 8秒
AWS Lambda のタイムアウト値: 7秒
この場合は、差分の1秒で AWS Lambda が再実行されてしまいます。

(参考)呼び出しの再試行ポリシー docs.aws.amazon.com

問い合わせフロー内の Lambda 呼び出しがスロットリングされた場合、リクエストは再試行されます。一般的なサービス障害 (500 エラー) が発生した場合も再試行されます。 同期呼び出しがエラーを返す場合、Amazon Connect は最大 8 秒間にわたり最大 3 回再試行します。この時点で、フローは Error ブランチを下に進みます。 Lambda 再試行の詳細については、「エラー処理と AWS Lambda での自動再試行」を参照してください。

1-4. 2回目の 外部 API の実行

AWS Lambda が再実行された結果、外部 API への処理も再実行されます。
その結果、外部 API の の2重実行となってしまいます。

事象の再現(Amazon Connect 側のタイムアウト値 > AWS Lambda のタイムアウト値)

実際に Amazon Connect と AWS Lambda のタイムアウト値を揃えないとどうなるか再現してみます。
Amazon Connect 側のタイムアウト値 > AWS Lambda のタイムアウト値 にして検証してみます。

Amazon Connect 設定

今回 使用する Amazon Connect の問い合わせフローは以下になります。

「AWS Lambda 関数を呼び出す」ブロックのタイムアウト値を8秒に設定します。

AWS Lambda 設定

コードは以下になります。 意図的にタイムアウトを発生させるために、7秒間 sleep しています。

import json
import time


def lambda_handler(event, context):
    print("Lambda 処理実行")
    # 止める
    time.sleep(7)
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

AWS Lambda のタイムアウト値を7秒に設定します。

実行結果

電話をして問い合わせフローの処理を実行していきます。 Cloud Watch Logs より、AWS Lambda が2回実行されていることが分かります。

回避策(Amazon Connect 側のタイムアウト値 == AWS Lambda のタイムアウト値)

Amazon Connect 設定

今度は、Amazon Connect の「AWS Lambda 関数を呼び出す」ブロックのタイムアウト値を7秒に設定し、AWS Lambda のタイムアウト値と揃えてみます。

実行結果

電話をして問い合わせフローの処理を実行していきます。 タイムアウト値を揃えて実行すると、AWS Lambda が1回しか実行されていないことが分かります。

まとめ

Amazon Connect と AWS Lambda を連携して外部 API を実行する時に、
AWS Lambda のタイムアウトを起因に複数回のリトライをさせたくない場合は、
Amazon Connect 側と AWS Lambda 側でタイムアウト値を揃えようという話でした。

最後に2つ注意点を書いて終わりにしたいと思います。

  • 注意点
    • 今回は予約系 API だったので複数回のリトライは困りますが、冪等性(何度繰り返しても同じ結果が得られる)が考慮されているシステムの場合は AWS Lambda をリトライさせた方が良いこともあります。
    • AWS Lambda が例外終了した場合はタイムアウト値を揃えても例外終了した時点でリトライされてしまうので、その点はご注意ください。


当記事は以上になります。どのたかのお役に立てれば幸いです!