はじめに
AWS Lambda を運用していると、CloudWatch のメトリクスやスロットリング、コールドスタートまわりで「あれ、これってどういう挙動だっけ?」と迷う場面が何度も出てきます。
その中でも、特によくある以下4つを整理しました。
- Lambda のメトリクス(Invocations / Errors / Throttles)の関係
- スロットリングエラーとは結局なにか
- 同時実行のスケーリングレート(バースト制限)の現状
- コールドスタートの対策と、その時間の調べ方
1. Lambda のメトリクス(Invocations / Errors / Throttles)
まず最初につまずきやすいのが、3つのメトリクスです。
各メトリクスの意味
| メトリクス | 何をカウントするか |
|---|---|
| Invocations | 関数コードが呼び出された回数。成功した呼び出しに加え、関数エラーになった呼び出しも含む。請求対象リクエスト数と一致する |
| Errors | 関数エラーが発生した呼び出しの数。コードがスローした例外に加え、タイムアウトや設定エラーなどランタイム由来のエラーも含む |
| Throttles | 同時実行数の上限に抵触してスロットリングされた呼び出しリクエストの数 |
ハマりやすいポイント:スロットリングはどこにもカウントされない
ここが一番の注意点です。スロットリングされたリクエストは、Invocations にも Errors にもカウントされません。
スロットリング発生
→ Throttles に +1
→ Invocations には入らない
→ Errors にも入らない
つまり「Throttles」と「Errors」は別物で、両方とも「Lambda がエラーになったとき」のメトリクスだと混同しがちですが、
- Throttles … 同時実行数の制限に抵触した場合にカウント
- Errors … それ以外の関数エラーが発生した場合にカウント
という棲み分けになっています。スロットリングは Errors には含まれないので、エラー監視を Errors メトリクスだけで組んでいると、スロットリングによる失敗を取りこぼします。
「エラーカウントと成功率」の計算式
マネジメントコンソールの関数モニタリングタブにある「エラーカウントと成功率(successRate)」は、Errors と Invocations から算出されています。計算式は次のとおりです。
成功率 = 100 - 100 × errors ÷ MAX(errors, invocations)
考え方はシンプルで、「100%(全部成功)から、エラーになった割合(%)を引く」 だけです。
成功率(%) = 100 − エラー率(%)
エラー率(%) = エラー数 ÷ 呼び出し数 × 100
たとえば、ある時間帯に呼び出しが 100 回・エラーが 5 回なら、
成功率 = 100 − (5 ÷ 100 × 100) = 100 − 5 = 95(%)
となります。
この式は、モニタリングタブの「エラーカウントと成功率」グラフ右上の i マーク(メトリクス情報)→「メトリクスで表示」 から CloudWatch Metrics に遷移すると確認できます。
なぜ分母が invocations ではなく MAX(errors, invocations) なのか
エラー率は普通に考えれば errors ÷ invocations で十分なはずです。ところが実際の式では、分母が 「errors と invocations のうち大きいほう」 になっています。
これは、集計の時間帯によっては errors が invocations を上回ってしまう ことがあるためです。なぜそんなことが起きるかというと、Errors メトリクスのタイムスタンプは「エラーが発生した時刻」ではなく「関数が呼び出された時刻」を基準に記録されるからです。たとえばタイムアウトした関数は、呼び出し開始から数十秒後にエラー判定されますが、その1件は「呼び出された時刻」の集計枠に入ります。こうしたズレが重なると、ある1分間の枠だけを見たときに errors > invocations という逆転が起こり得ます。
このとき素直に errors ÷ invocations で計算すると、エラー率が 100% を超え、成功率がマイナスになってしまいます。それを防ぐために、分母を MAX(errors, invocations) にして エラー率が 100% を超えない(=成功率が 0% を下回らない) ようにしているわけです。
2. スロットリングエラーとは何か
「スロットリングエラー」という言葉が指しているのは、ざっくり言うと 同時実行数の上限に引っかかって、リクエストが拒否された状態 です。
Lambda には「同時に実行できる関数の数」に上限があります。すべての実行環境インスタンスがリクエストを処理中で、かつそれ以上スケールアップできる余地がないとき、Lambda は追加のリクエストを TooManyRequestsException エラーで拒否します。これがスロットリングです。
同時実行数(Concurrency)の基本
- 同時実行数 = Lambda 関数が同時に処理できる「未完了のリクエスト」の数
- Lambda は同時実行リクエストごとに、実行環境の個別インスタンスをプロビジョニングする
- リクエストが増えると、アカウントの同時実行上限に達するまで Lambda が自動でスケールする
- デフォルトの同時実行上限は 1リージョン内の全関数合計で 1,000
重要な関数でスロットリングを起こさないためには、
- クォータ引き上げ(同時実行数の上限緩和)をリクエストする
- 関数レベルで同時実行のコントロール(予約された同時実行など)を設定する
といった対策があります。
3. 同時実行のスケーリングレート(バースト制限)の現状
スロットリングと密接に関わるのが、**同時実行のスケーリングレート(バースト制限)**です。
「同時実行のスケーリングレート」とは、ざっくり言うと 「リクエストが急増したときに、Lambda がどれくらいの速さで実行環境を増やせるか」を表す速度 のことです。
少しかみ砕くと、こういう関係になっています。
- 「同時実行数」 … 同時に処理できるリクエストの上限の量(例: 1,000)
- 「スケーリングレート」 … その上限に向かってどれくらいの速さで増やせるか(一定時間あたりに追加できる実行環境の数)
たとえば同時実行数の上限が高くても、増やすスピード(スケーリングレート)が遅ければ、急なアクセス集中には追いつけずスロットリングが起こります。逆に言うと、スパイク的なトラフィックに耐えられるかは、この「速度」のほうが効いてくる、ということです。
このスケーリングレートは過去に大きな仕様変更があり、古い情報が出回っている要注意ポイントなので、以下で経緯を整理します。
古い情報:リージョンごとに差があった(〜2023年)
2020年頃の資料では、最初の1分間のバースト同時実行がリージョンによって異なるとされていました。
| 値 | 対象リージョン |
|---|---|
| 3000 | 米国西部(オレゴン)、米国東部(バージニア北部)、欧州(アイルランド) |
| 1000 | アジアパシフィック(東京)、欧州(フランクフルト) |
| 500 | その他のリージョン |
このため当時は「東京リージョンより、バースト性能の高いオレゴンやバージニア北部で構築したほうが有利では?」という設計判断があり得ました。
現在:リージョンによる差はない(2023年12月〜)
2023年12月のアップデートでこの仕様は変わりました。 現在は次のとおりです。
- 各 AWS リージョン・各関数において、スケーリングレートは 10秒ごとに 1,000 の実行環境インスタンス(= 10秒ごとに 1秒あたり 10,000 リクエスト)
- リージョンによる差はない
- 各関数が互いに独立してスケールするようになった(旧仕様ではアカウント内の関数でスケーリング上限を共有しており、いわゆる「ノイジーネイバー」問題があった)
このアップデートは「Lambda 関数が大量リクエストを処理する際に最大12倍速くスケールできるようになった」というもので、追加コストや既存関数の設定変更は不要でした。
結論として、同時実行数・バースト制限について「リージョンごとに性能が異なる」ということは現在はありません。 東京リージョンか他リージョンか、という設計判断にスケーリング性能の観点を持ち込む必要はなくなっています。
参考:
スケーリングレートは上限緩和できない
注意点として、同時実行スケーリングレート(増やす速さ)そのものは、上限緩和の申請ができません。 これは固定の制限です。
ここで「同時実行数の上限を緩和すれば、スケーリングも速くなるのでは?」と思うかもしれませんが、この2つは別物で、同時実行数の上限緩和でスケーリングレートが速くなることはありません。
- 同時実行数 … 同時に処理できるリクエストの「上限の量」(天井の高さ)
- スケーリングレート … その上限に向かって「どれくらいの速さで増やせるか」(増やすスピード)
同時実行数の上限を引き上げると「天井」は高くなりますが、そこへ到達するスピード(スケーリングレート)は変わりません。Lambda は同時実行数の上限をいくつに設定していても、1関数あたり10秒ごとに1,000インスタンスずつしか増やせないからです。
そのため、スロットリングには原因が2種類あり、効く対策も異なります。
| スロットリングの原因 | どういう状態か | 有効な対策 |
|---|---|---|
| 同時実行数の上限に到達 | 天井(量)に達して、これ以上同時に処理できない | 同時実行数の上限緩和が有効 |
| スケーリングレートに追いつかない | リクエストの急増スピードに、環境の増設が間に合わない | 上限緩和では解決しない(レートは固定) |
ゆるやかにアクセスが増えて最終的に上限の「量」に達したのであれば、同時実行数の上限緩和で改善します。一方、短時間に一気に跳ね上がるスパイクでは、たとえ上限を引き上げても環境の増設が追いつかずスロットリングし得ます。この場合は上限緩和では解決せず、「プロビジョニングされた同時実行」で事前に実行環境を用意しておくなど、別のアプローチが必要になります。
なお、その同時実行数(Concurrency)の上限緩和については、デフォルト 1,000 から数万まで引き上げが可能です。ただし、
- リージョンごとに「どこまで緩和できるか」の公開情報はない
- 上限緩和は担当部署がユースケースをもとに個別判断するため、緩和可能な最大値は案内されない
という運用になっています。
参考:
4. コールドスタートの対策
ここからはコールドスタートの話です。
予約された同時実行 vs プロビジョニングされた同時実行
名前が似ていて混同しやすい2つの設定ですが、コールドスタート対策に効くのは「プロビジョニングされた同時実行」のほうだけです。
| 予約された同時実行 (Reserved Concurrency) |
プロビジョニングされた同時実行 (Provisioned Concurrency) |
|
|---|---|---|
| 役割 | 関数専用に一定数の同時実行を「確保」する | 事前に初期化済みの実行環境を割り当てる |
| 実行環境の初期化 | 事前初期化されない | 事前に初期化される |
| コールドスタート | 発生しうる(呼び出し時に初期化が走る) | 発生しない(常にウォームスタート) |
| 主な用途 | 他関数に同時実行を食われないよう枠を確保したい | コールドスタートを緩和したい |
ポイントを整理すると、
- 予約された同時実行 … 関数のために常に一定数の同時実行枠を確保したいときに使う。ただし環境は事前初期化されないため、呼び出し時に初期化が走り、コールドスタートは起こり得る。
- プロビジョニングされた同時実行 … 指定した数だけ実行環境を事前に初期化しておく機能。呼び出し前に Init フェーズを完了させておくので、常にウォームスタートになる。
したがって、コールドスタート緩和が目的なら「プロビジョニングされた同時実行」を設定します。
なお、予約された同時実行・プロビジョニングされた同時実行のどちらも、アカウントの同時実行上限(リージョン別クォータの「同時実行数」)にカウントされます。「予約された同時実行」「プロビジョニングされた同時実行」自体にサービスクォータの設定はありませんが、それらが消費する「同時実行数」のクォータは上限緩和の申請が可能です。
参考:
プロビジョニングされた同時実行の前提:バージョン / エイリアス
プロビジョニングされた同時実行を設定するには、修飾子(バージョンまたはエイリアス)の指定が必須です。設定画面でも「修飾子のタイプ(エイリアスまたはバージョン)を選択」というステップがあります。
もし運用上の理由でバージョン / エイリアスを使えない場合は、その「使えない理由・背景」と「想定している代替策」を整理したうえで、AWS サポートに具体的な要件を共有して相談するのが現実的です(要件次第で取り得る選択肢が変わるため)。
5. コールドスタートの時間を調べる
「コールドスタートにどれくらい時間がかかっているか」を測るには、CloudWatch Logs の REPORT 行を見ます。
REPORT 行の Init Duration と Duration
REPORT 行のデータフィールドには、調査に使える値が出力されます。
- Duration … 関数の処理にかかった実行時間(ハンドラーの処理時間)
- Init Duration … 初期化にかかった時間。コールドスタート時にのみ出力される
「Lambda が起動し始める前(コールドスタート)+ Lambda の起動時間」を調べたい場合は、この Init Duration と Duration を組み合わせて見るのが有効です。
Init Duration = コールドスタートの時間、ではない
ここが地味に重要な落とし穴です。「Init Duration を見ればコールドスタートの時間がわかる」と思いがちですが、この2つは指している範囲が違います。
まず、新しい実行環境が呼び出しに応えるまでには、次の4ステップが順番に行われます。
| ステップ | 内容 | 区分 |
|---|---|---|
| 1 | 実行環境に関数のコードをダウンロードする | 環境の準備 |
| 2 | メモリ・ランタイム・各種設定に基づいて環境を作成する | 環境の準備 |
| 3 | ハンドラー外に書かれた初期化コード(Function init)を実行する | 自分のコードの初期化 |
| 4 | ハンドラーコードを実行する(=関数本体の処理) | 本処理 |
ここで「コールドスタート」と「Init Duration」がそれぞれカバーする範囲を並べると、こうなります。
ステップ: 1 ──── 2 ──── 3 ──── 4
コールドスタート: ├──────────┤ ← ステップ 1〜2 のみ
Init Duration: ├──────────────────┤ ← ステップ 1〜3(初期化コードまで含む)
Duration: ├──┤ ← ステップ 4(ハンドラーの実行時間)
- 一般に「コールドスタート」と呼ばれるのは ステップ 1〜2(コードのダウンロードと環境作成)だけ
-
Init Durationが表すのは ステップ 1〜3。つまりコールドスタートに加えて、自分で書いた初期化コードの実行時間(ステップ3)も含んでいる
そのため、Init Duration の値はコールドスタートの時間そのものではなく、それより大きい値になります。差分のステップ3は「ハンドラー外に書いた処理(DB接続の確立、設定の読み込みなど)」なので、ここが重ければ Init Duration はその分ふくらみます。
逆にこれは、Init Duration が大きいときに「AWS側のコールドスタートが遅い」のか「自分の初期化コードが重い」のかを切り分ける手がかりにもなります。コールドスタート対策の効果測定をするときは、この違いを意識しておくと数字の解釈を誤りません。
参考:
まとめ
最後に、この記事の要点を箇条書きで振り返ります。
-
スロットリングは Invocations にも Errors にもカウントされない。
Throttlesという独立したメトリクスに記録される。エラー監視をErrorsだけで組むとスロットリングを取りこぼす。 - 成功率は
100 - 100 * errors / MAX([errors, invocations])で算出される。 -
スロットリングエラー = 同時実行数の上限に抵触してリクエストが拒否された状態(
TooManyRequestsException)。 - 同時実行のスケーリングレートは、2023年12月以降はリージョンによる差がない(10秒ごとに 1,000 インスタンス)。リージョン選定にスケーリング性能の観点は不要。
- スケーリングレートは上限緩和不可。一方、同時実行数(Concurrency)はクォータ引き上げ可能。
- コールドスタート対策に効くのは「プロビジョニングされた同時実行」。「予約された同時実行」は枠の確保であり、事前初期化はされない。
- プロビジョニングされた同時実行にはバージョン / エイリアスの指定が必須。
- コールドスタートの計測は CloudWatch Logs の REPORT 行の
Init Duration/Durationで。ただしInit Durationは初期化コードの実行時間も含むため、コールドスタートそのものの時間ではない点に注意。
メトリクスの「重複しない関係」と、スケーリングまわりの「古い情報との差分」を押さえておくと、Lambda の運用やトラブルシュートがだいぶ楽になります。