急激なリクエスト増が発生した場合のAWS Lambdaの挙動を確認する(1)

### はじめに
 AWS LambdaはAWSの様々なAWSサービスをイベントソースとしてシームレスに連携して起動可能なサーバーレスなコンピュートサービスです。カタカナばかりで分かりにくいかもしれませんので別の表現をすると、
`「AWS Lambdaは様々なAWSサービス(、例えば、Amazon S3,Amazon API Gateway, ELB( Application Load Balancer), Amazon SQS, Amazon SNS, Amazon DynamoDB (Streams)等)をイベントソース（入力元、起動のトリガー）としてシームレスに連携して起動可能なサーバーレス(サーバの運用や**サーバのスケール**等の運用が不要な、アプリケーション開発者が開発をしやすくすることができる)コンピュート（アプリプログラム実行環境）`サービスです

上述する通り、サーバーのスケールの考慮が不要と記載がありましたが、AWS Lambdaがどのようにスケールするか仕様を確認したうえで、ご自身のワークロードに適しているか判断してご利用いただくことが重要です。

余談ですが、過去に私が受けた相談の中でも「AWSのXXXサービスをつかって作りたいんだけど？」というご相談があります。AWS ではコンピュート、ストレージ、データベース、ネットワーク、セキュリティなどをはじめとした175以上のサービスを提供しており、**お客様の目的（機能要件・非機能要件）に合わせてサービスを選択する**ことが重要であり、当然、AWSサービスありきではありません。（もちろん学習目的という理由で使うというケースも中にはあります）

さて、今回は、リクエストが急増することが想定される場合にAWS Lambdaがどのように挙動するかを確認していきたいと思います。
### サマリ
- 同時実行数のバーストには制約があるため、注意が必要
- 同期・非同期・ポーリング系（キュー、ストリーミング）で動きが異なる
- スロットリングは皆様を守る大切な仕組み。うまく付き合う。
 

### 急増の定義
急増、つまり急激な「増加」とは何を指すのか認識合わせが重要です。
1 rps(resuest per second)だったものが1000 rps になる場合、最終的に1秒あたりのリクエスト数が、999件のリクエストが増えたことになります。当記事では、「どれくらいの期間かけて増えたのか」、つまり、単位時間当たりの増分がポイントになります。これが2時間かけて緩やかに増えていけば、rps自体は1000倍という大きな値ですが、2時間(=1200分)で平均的に増えていった場合、増加分は1分当たり1リクエストとなります。負荷状態は1000倍となり私は高負荷であると考えますが、急激な増加か？といわれると単に1件ずつ増えただけとなります。

当記事における急増は、単位時間当たり**増加分**が大きい値の場合を対象とします。「大きい値」はどれくらい？と疑問に思う方もいるでしょう。人によって、大きい、小さいが異なりますから。ここでは、後述の初期バーストの値を超えるリクエストが来た場合ということにして、1000以上と定義します。（ケースによっては500以上）

### AWS ドキュメントによるLambda関数のスケールの仕様
AWSドキュメントのこちらの文書、[AWS Lambda 関数スケーリング](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-scaling.html)でスケーリングの仕様を説明しています。図もあって増え方について説明が書かれているので一度ご確認ください。

#### 抑えるべき概念
**1. 同時実行数**
あるタイミングで動いているLambda関数のインスタンス数のこと。  
処理時間が1秒かかるLambda関数(CloudWatchメトリクスでdurationが1000msとなる関数）は毎秒1件ずつ起動する場合を考えててみます。この場合は、Lambda関数の同時実行数は1となります。なぜなら、最初のリクエストが1秒で完了し、完了と同時に2個目のリクエストが処理されるため、同時に実行されてるのは1となります。もちろん、実際のワークロードでは常に1秒で終わるということはなく、1.1秒かかることもあれば、0.9秒で終わることもあります。1.1秒かかった場合は0.1秒間、同時実行数が2となります。そして、もし、5秒かかる関数であれば、毎秒1件ずつ起動する場合は最大の同時実行数は5となります。

AWS Lambdaでは、各AWSアカウントのリージョン別に最大同時実行数を定めており（上限緩和可能）**最大同時実行数を超えてLambda関数のインスタンスを起動することはできません**。 その場合、起動要求があった場合はHTTPステータス429が応答されます。エラーということです。

**2. 初期バーストの制限**
  AWS Lambdaはスケール（関数のインスタンスう数の増減）を自動的に実施します。 Lambda関数のインスタンスを増やす考え方として、「初期（最初）」と「それ以降」の二種類があります。増え方が違います。初期バースト数は2020/06/23時点では以下の通りです。 

- 3000 – 米国西部 (オレゴン)、米国東部（バージニア北部）、欧州 (アイルランド)
- 1000 – アジアパシフィック (東京)、欧州 (フランクフルト)
- 500 – その他のリージョン

では、この初期バースト数は何を意味するのでしょうか。これは、最初のバーストとして最大で上記の値まではインスタンス数が増加するということを意味します。ただ、別の項目による制約があります（後述の同時実行数の予約）
例えば東京リージョンの場合、初期バースト数は1000です。仮にある関数に同時実行数3000が必要で初期に0の状態だった場合、まず、1000まではインスタンスが起動されます。1000まで増えた後、足りない2000(3000-2000)をAWS Lambdaがどのように増やしていくのかについては次の項目確認してください。

**3. 初期バースト後のバースト数**
初期バースト以降の増え方はどうなるのでしょうか。初期バースト後、まだ同時実行数が不足（つまり、リクエスト数に対応できる同時実行数分の関数がまだない場合）している場合、追加で関数のインスタンスを増やす動きになります。ただし、増え方は全リージョン共通で、1分間に500インスタンスとなります（この値は固定であり、上限緩和できません）。つまり、足りない2000インスタンス分は、4分かけてインスタンス数が増える形となります。
注意：[Amazon Kinesis Data Streams](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-kinesis.html#services-kinesis-configure) /[Amazon DynamoDB Streams](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-ddb.html)/[Amazon SQS](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs.html)をイベントソースにする場合の関数の増加は別途、定義されていますのでリンクを確認してください）

各Lambda関数が必要とする同時実行数分のインスタンスが存在しない場合、つまり、処理中のリクエスト数をLambda関数のインスタンス数が下回る場合、リクエストに対して HTTP ステータス429が応答されます。
これはスロットリングと呼ばれるもので、バースト時以外にも発生します。

***4. 同時実行数の予約***
[同時実行数の予約](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-concurrency.html)を使うことで、各Lambda 関数には同時実行数の予約が可能です。その値を超えたリクエストが来た場合、スロットリングになります。
また、「同時実行数の予約」はデフォルトでは個々の関数にはされていません。ただ、該当リージョン内で別のAWS Lambda関数が同時実行数の予約をしている場合、同時実行数の予約を未指定の他の関数群はリージョンの「同時実行数」からリージョン内のLambda関数で予約済みの同時実行数を引いた値が上限となります。それを超えたリクエスト数が未予約の関数全体で来た場合、スロットリングになります。
なお、関数ごとにも同時実行数（後述の同時実行数の予約）を指定することができますが、前述のリージョン別の同時実行数以下の値となります。


### スケールとスロットリング ###
**スロットリング**
スロットリングはAWSサービスを利用するうえで必ず押さえるべき概念です。簡単にいうと、「各AWSサービスの仕様や、アカウント所有者等の利用者により設定された値以上のリクエストはエラーにする」というものです。スロットリングの閾値は、サービスやAPIごとによって異なるため各サービスのサービスクオータ等をご確認ください。なお、多くのサービスがスロットリングは従量課金制をとっているAWSサービスにおいて皆様の予期せぬリソース利用によるAWSサービス利用料の増加に対しての防御策の1つになるものです。ですので必要な仕組みと理解いただいた上で、上手にお付き合いいただければと思います。

**Lambda関数とスロットリング**
Lambda関数ではいくつかの要素を組み合わせてスロットリングが判定されます。それは前述の以下の項目も含まれます。
- 「同時実行数」を超えるリクエスト
- 「同時実行数の予約」を超えるリクエスト
- 「初期バーストの制限」や「初期バースト後のバースト数」を超えるリクエスト


1. 同時実行数を超えるリクエスト
　これは、個々のAWSアカウントの各リージョンごとに決まっている値です。**同時実行数を超えた場合**、スロットリングになります。

2. [同時実行数の予約](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-concurrency.html)以上のリクエスト
前述のとおり、同時実行数の予約をした関数はそれ以上の処理ができませんし、一方で未指定の関数は、リージョン内の同時実行数から、予約済みの同時実行数を引いた値以上のリクエストは処理できません。スロットリングになります。

3. 初期バースト発生後
前述したバースト（初期とその後）の値以上のインスタンス数の増加、つまり、**急激なリクエスト増が発生し、処理する関数の同時実行数分のインスタンス数が追い付いていない場合** スロットリングになります。

4. 呼び出し頻度
[こちら](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html)の**「リージョンあたりの呼び出し頻度 (リクエスト数/秒)」** に記載があります。同期・非同期か、また、非同期の場合のイベントソースはAWSサービスが否かによって、リクエストの受付数（同時実行数ではない）に上限があります。


### スロットリングへの対応や回避する方法
スロットリングに関連したトラブルシューティングは[こちら](https://aws.amazon.com/jp/premiumsupport/knowledge-center/lambda-troubleshoot-throttling/)で解説されていますので参考にしてみてください。
回避する方法は様々な方法があります。

1. リトライ
同期呼び出しの場合は[こちらの考え方](https://docs.aws.amazon.com/ja_jp/general/latest/gr/api-retries.html)を取り入れたリトライ処理をクライアント側で必ず組み込むようにしてください。リトライしてもスロットリングになる可能性もあります。
非同期呼び出しの場合は、1分から6時間（要設定）の中でリトライを実施します。

2. Durationの最小化によるインスタンス当たりのスループット向上
同時実行数はその関数がそのタイミングで同時に動いている関数です。10秒かかっていた処理を1秒に短くできれば、事実上、同時実行数が10倍になります。Lambdaに割り当てるメモリを増やすこと、外部システムアクセスや時間がかかる処理の非同期化（Amazon SQSやKinesis Data Streamsをその処理呼び出しの際に活用する）を検討しでLambda関数が動いている時間を最小化する工夫をしてください。
App->lambda->**Asynchronous**->external

3. バースト数の最小化（事前に関数のインスタンスを用意）
[Provisioned Concurrency](https://aws.amazon.com/jp/about-aws/whats-new/2019/12/aws-lambda-announces-provisioned-concurrency/)という機能が2019年末に登場しました。こちらを使うことで対象関数を事前に暖機（つまり、その関数の同時実行数を増やすこと）が可能になります。こちらの機能を利用して、バーストを極力起きないようにすることで影響を緩和できます。
なお、Provisioned Concurrencyで増やせる数はアカウントの最大同時実行数を超えることはできません。

4. 同期処理の場合は非同期化
上述の「Durationの最小化によるインスタンス当たりのスループット向上」内では、Lambdaから呼び出す別の処理を非同期化することを例示しました。ここでは、Lambda関数そのものの呼び出しについてを非同期化することを述べています。非同期化することでのメリットは、呼び出し頻度の制約を受けている場合、それを回避することができるという点です。
 App -> **Asynchronous**->Lambda->(Asynchronous(上述))-?External  

5. 上限緩和申請
同時実行数がアプリケーションの特性上、必要だ！という場合は、上限緩和申請をしていただくことで同時実行数を増やすことが可能な場合があります。現行のアーキテクチャを分析し改善できるよう要素等がないかご確認の上、申請してみてください。


### おまけ、スロットリングをうまく使う。
　同時実行数の予約を利用して、該当関数をゼロにすると、その関数は起動されません。つまり、後続でなんらかエラーが発生して処理を一時的に止めたい場合、ゼロにすることで実現できます。また、Lambda関数を無限ループで呼び出すアプリがいた場合、呼び出される側を0にすることで、実行されないようにすることも可能です。その結果、下流のシステムを保護することにもつながります。うまくスロットリングに関連する機能を使ってみてください。