Amazon Cognito の新機能「Provisioned Limit API」を実際に試してみた
はじめに
Amazon Cognito User Pools の API レート上限は、2 層のクォータで管理されています。1 つは AWS アカウントおよびリージョンごとに設定される上限(Account-level max limit)で、もう 1 つはその範囲内で調整できる Provisioned limit です。Provisioned limit は UpdateProvisionedLimit API で即時に変更できますが、上限自体を引き上げるにはサービスクォータの申請が必要です。
いわさの記事では Provisioned Limit を操作する API そのものを紹介しています。本記事ではその続きとして、実際にサービスクォータの引き上げを申請し、承認後に UserCreation の上限を 50→75 RPS へ引き上げました。Go による並列負荷テストでスループットの改善幅を測定しました。
検証内容
サービスクォータ引き上げ申請
まず引き上げ前の状態を確認しました。GetProvisionedLimit で UserCreation カテゴリの現在値を取得します。
aws cognito-idp get-provisioned-limit \
--region ap-northeast-1 \
--limit-definition '{"LimitClass":"API_CATEGORY","Attributes":{"Category":"UserCreation"}}'
ProvisionedLimitValue と FreeLimitValue がともに 50 でした。
{
"Limit": {
"LimitDefinition": {
"LimitClass": "API_CATEGORY",
"Attributes": {
"Category": "UserCreation"
}
},
"ProvisionedLimitValue": 50,
"FreeLimitValue": 50
}
}
この状態で Provisioned limit を 50 より大きい値に変更するには、先に Account-level max limit のサービスクォータ引き上げが必要です。Service Quotas で UserCreation の上限を 75 にリクエストしました。クォータコードは L-5987B8A0(Rate of UserCreation requests)です。
aws service-quotas request-service-quota-increase \
--region ap-northeast-1 \
--service-code cognito-idp \
--quota-code L-5987B8A0 \
--desired-value 75
リクエストは PENDING ステータスで受理されました。
{
"RequestedQuota": {
"ServiceName": "Amazon Cognito User Pools",
"QuotaName": "Rate of UserCreation requests",
"DesiredValue": 75.0,
"Status": "PENDING",
"Created": "2026-07-03T16:54:34+09:00"
}
}
request-service-quota-increase の出力全文
{
"RequestedQuota": {
"Id": "9e1e8001-example-request-id",
"ServiceCode": "cognito-idp",
"ServiceName": "Amazon Cognito User Pools",
"QuotaCode": "L-5987B8A0",
"QuotaName": "Rate of UserCreation requests",
"DesiredValue": 75.0,
"Status": "PENDING",
"Created": "2026-07-03T16:54:34+09:00",
"Requester": "{\"accountId\":\"123456789012\",\"callerArn\":\"arn:aws:sts::123456789012:assumed-role/example-role/example-user\"}",
"QuotaArn": "arn:aws:servicequotas:ap-northeast-1:123456789012:cognito-idp/L-5987B8A0",
"GlobalQuota": false,
"Unit": "None",
"QuotaRequestedAtLevel": "ACCOUNT"
}
}
このサービスクォータの引き上げはサポートケース経由でサービスチームとのやり取りが発生し、ユースケースの説明や希望する上限値・対象・利用期間の確認を求められ、週末を 2 回跨いだこともあり、10 日以上を要しました。
Limit 引き上げ確認
承認後、UpdateProvisionedLimit で Provisioned limit を 75 に設定しました。
aws cognito-idp update-provisioned-limit \
--region ap-northeast-1 \
--limit-definition '{"LimitClass":"API_CATEGORY","Attributes":{"Category":"UserCreation"}}' \
--requested-limit-value 75
GetProvisionedLimit で変更後の状態を確認したところ、ProvisionedLimitValue が 75 に更新されていました。FreeLimitValue は 50 のままで、超過分の 25 RPS が課金対象になります。
{
"Limit": {
"LimitDefinition": {
"LimitClass": "API_CATEGORY",
"Attributes": {
"Category": "UserCreation"
}
},
"ProvisionedLimitValue": 75,
"FreeLimitValue": 50
}
}
負荷テスト
引き上げ効果を測定するため、Go で AdminCreateUser を goroutine 並列実行するツールを作成しました。1 回のテストで 1000 ユーザーの作成を試みます。スロットリングを直接カウントするため、SDK のリトライは無効化し、TooManyRequestsException の発生件数を記録する構成です。
テスト環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| インスタンス | EC2 c8g.xlarge(Graviton4, 4 vCPU, 8 GiB) |
| OS | Amazon Linux 2023 (ARM64) |
| 言語 | Go 1.22 |
| SDK | AWS SDK for Go v2 |
| HTTP | SDK デフォルト |
| 並列制御 | goroutine + channel ベースのセマフォ |
| リトライ | 無効(retry.AddWithMaxAttempts(retry.NewStandard(), 1)) |
| リージョン | ap-northeast-1(東京) |
テストコードは以下のとおりです。
負荷テストコード(Go)
package main
import (
"context"
"errors"
"flag"
"fmt"
"os"
"sync"
"sync/atomic"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
)
func main() {
concurrency := flag.Int("concurrency", 10, "Number of concurrent goroutines")
totalUsers := flag.Int("users", 1000, "Total number of users to create")
userPoolID := flag.String("user-pool-id", "", "Cognito User Pool ID")
region := flag.String("region", "ap-northeast-1", "AWS Region")
flag.Parse()
prefix := fmt.Sprintf("loadtest-%d", time.Now().Unix())
// Load AWS config with NO retries (we want to measure throttling directly)
cfg, err := config.LoadDefaultConfig(context.Background(),
config.WithRegion(*region),
config.WithRetryer(func() aws.Retryer {
return retry.AddWithMaxAttempts(retry.NewStandard(), 1)
}),
)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load AWS config: %v\n", err)
os.Exit(1)
}
client := cognitoidentityprovider.NewFromConfig(cfg)
var (
successCount int64
throttleCount int64
latencies []time.Duration
latencyMu sync.Mutex
)
sem := make(chan struct{}, *concurrency)
var wg sync.WaitGroup
startTime := time.Now()
for i := 0; i < *totalUsers; i++ {
wg.Add(1)
sem <- struct{}{}
go func(seq int) {
defer wg.Done()
defer func() { <-sem }()
username := fmt.Sprintf("%s-%04d", prefix, seq)
reqStart := time.Now()
_, createErr := client.AdminCreateUser(context.Background(),
&cognitoidentityprovider.AdminCreateUserInput{
UserPoolId: userPoolID,
Username: aws.String(username),
MessageAction: types.MessageActionTypeSuppress,
})
elapsed := time.Since(reqStart)
if createErr != nil {
var tooMany *types.TooManyRequestsException
if errors.As(createErr, &tooMany) {
atomic.AddInt64(&throttleCount, 1)
}
} else {
atomic.AddInt64(&successCount, 1)
latencyMu.Lock()
latencies = append(latencies, elapsed)
latencyMu.Unlock()
}
}(i)
}
wg.Wait()
totalDuration := time.Since(startTime)
// Calculate percentiles and output results...
}
Limit=50 と Limit=75 それぞれについて、並列数を変えて実行しました。
結果
負荷テストの結果は以下のとおりです。計測回数 1 回の簡易検証ですが、引き上げ効果は実効 RPS がデフォルト Limit 付近に達する負荷帯で最も顕著でした。
並列数 10(実効 RPS 約 33)
| Limit | 成功 | スロットル | 実効RPS | Duration | p50 (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|---|---|---|---|
| 50 | 1000 | 0 | 32.52 | 30.75s | 293.01 | 400.52 | 702.82 |
| 75 | 996 | 4 | 34.30 | 29.04s | 287.32 | 337.10 | 361.56 |
Limit=50・75 のどちらもスロットルはほぼ発生しませんでした。実効 RPS がデフォルト Limit に達しない負荷帯です。ただし Limit=75 の試行で 4 件のスロットルが発生しており、短時間のリクエスト集中により瞬間的に Limit 付近に達した可能性があります。
並列数 20(実効 RPS 約 65〜70)
| Limit | 成功 | スロットル | 実効RPS | Duration | p50 (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|---|---|---|---|
| 50 | 689 | 311 | 64.43 | 10.69s | 287.50 | 393.02 | 473.02 |
| 75 | 1000 | 0 | 69.73 | 14.34s | 277.38 | 336.68 | 413.25 |
明確な差が出ました。Limit=50 では 311 件がスロットルされましたが、Limit=75 に引き上げると成功数は 1000 件、スロットルは 0 件と完全に解消しました。実効 RPS がデフォルト Limit の 50 を超えて上限に張り付く負荷帯です。
並列数 50(実効 RPS 約 137〜158)
| Limit | 成功 | スロットル | 実効RPS | Duration | p50 (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|---|---|---|---|
| 50 | 386 | 614 | 137.13 | 2.82s | 303.49 | 455.73 | 492.47 |
| 75 | 513 | 487 | 157.95 | 3.25s | 278.83 | 354.73 | 384.73 |
Limit=75 でもスロットルが発生しました。スロットルを含む総試行レートは 1000 / 3.25s ≒ 308 req/s であり、引き上げ後の Limit 75 を大きく上回っているためです。それでも実効 RPS は約 137 から約 158 へ向上し、成功数も 386 件から 513 件へ増加しました。Limit の引き上げ分が成功スループットの差として表れています。
まとめ
今回の検証では、Amazon Cognito の UserCreation に対する Provisioned Limit を 50→75 RPS に引き上げることで、Cognito 起因のスロットリングが発生していた負荷帯において、成功スループットの改善を確認できました。Provisioned Limit とサービスクォータ引き上げは、Cognito の API レート上限がボトルネックになっている場合の有効な解決手段です。
ただし、実際のシステムでは Cognito 以外にも、アプリケーション側の並列制御、SDK のリトライ、HTTP クライアント、ネットワーク、下流処理などがボトルネックになる可能性があります。まずはピーク負荷を再現したテストで、どこで詰まっているのかを切り分けることが重要です。
また、Account-level max limit の引き上げにはサービスクォータ申請が必要で、承認までに時間がかかる場合があります。大規模なユーザー登録やサインインが見込まれる場合は、Cognito がボトルネックだと分かってから慌てて申請するのではなく、余裕を持ったスケジュールで検証と申請を進めることをおすすめします。







