Skip to content

Full Login Flow (Mail OTP) Load Test

Test Overview

ItemDetails
Test DateDecember 17, 2025
Target FlowComplete OAuth 2.0 login with Mail OTP
PurposeMeasure end-to-end login performance including OTP verification
ShardingRegion Shards 32, Code Shards 8, Refresh Token 64

Executive Summary

With 32-shard optimization, DO errors and latency spikes from the previous test (Dec 13) were completely eliminated. Stable operation confirmed at 150 LPS (Logins Per Second).

Target LPSCompleted FlowsSuccess RateP50P95P99DO P99Status
50 LPS7,312100%587ms688ms748ms2,846ms⚠️ Cold start
100 LPS14,624100%570ms696ms753ms1,393ms✅ Good
125 LPS18,232100%621ms761ms826ms1,153ms✅ Good
150 LPS21,937100%639ms756ms853ms955ms✅ Excellent

Comparison: Dec 13 vs Dec 17

MetricDec 13 (16 shards)Dec 17 (32 shards)Improvement
100 LPS DO Errors443 (0.39%)0100% eliminated
150 LPS P953,015ms283ms91% reduction
150 LPS DO P991,648ms955ms42% reduction

Test Environment

K6 Cloud Configuration

Parameter50 LPS100 LPS125 LPS150 LPS
Executorramping-arrival-rateramping-arrival-rateramping-arrival-rateramping-arrival-rate
Test Duration3 min3 min3 min3 min
Preallocated VU200400500600
Max VU4006008001000
Seed Users500100012501500
Load Zoneamazon:us:portlandamazon:us:portlandamazon:us:portlandamazon:us:portland

Sharding Configuration

StoreShardsGeneration
SessionStore32Gen 2
ChallengeStore32Gen 2
AuthCodeStore32Gen 2
RevocationStore32Gen 4
RefreshTokenRotator64Gen 4

Test Methodology

Full Login Flow (5 Steps)

flowchart LR
    A["1. AuthorizeInit
(GET /authorize)"] --> B["2. OTP Generate
(POST /email-code/generate)"] B --> C["3. OTP Verify
(POST /email-code/verify)"] C --> D["4. AuthorizeCode
(GET /authorize prompt=none)"] D --> E["5. Token
(POST /token)"]

Step Descriptions

  1. AuthorizeInit: OAuth 2.0 authorization request, display login page
  2. EmailCodeGenerate: Generate & store OTP code (ChallengeStore)
  3. EmailCodeVerify: Verify OTP code, issue session (SessionStore)
  4. AuthorizeCode: Issue authorization code (AuthCodeStore)
  5. Token: Issue access token and ID token

Success Criteria

  • P95 Latency < 5,000ms
  • Success Rate > 95%

Results - Performance Metrics

50 LPS Test

Test Period: 2025-12-16T16:01:30Z - 16:06:30Z UTC

MetricValue
Worker Total Requests30,478
Worker P50 Duration22.6ms
Worker P99 Duration46.7ms
DO Total Requests45,688
DO Errors0
DO Wall Time P5046ms
DO Wall Time P992,846ms
D1 Read Queries1,143,239
D1 Write Queries28,478

⚠️ Note: High DO P99 due to cold starts. At 50 LPS with 32 shards, each shard receives only 1.56 req/s.

100 LPS Test

Test Period: 2025-12-16T16:09:00Z - 16:14:00Z UTC

MetricValue
Worker Total Requests59,761
Worker P99 Duration67.4ms
DO Total Requests89,531
DO Errors0
DO Wall Time P991,393ms
D1 Read Queries1,169,929
D1 Write Queries41,785

Good: Zero DO errors (vs 443 on Dec 13)

125 LPS Test

Test Period: 2025-12-16T16:15:50Z - 16:21:00Z UTC

MetricValue
Worker Total Requests74,176
Worker P99 Duration88.4ms
DO Total Requests111,184
DO Errors0
DO Wall Time P991,153ms
D1 Read Queries1,209,902
D1 Write Queries61,789

Good: DO Wall Time P99 continues to improve

150 LPS Test

Test Period: 2025-12-16T16:23:00Z - 16:28:00Z UTC

MetricValue
Total Requests89,000
HTTP Failures0
Peak RPS598 req/s
P95 Response Time283ms
Worker P99 Duration69.2ms
DO Total Requests133,400
DO Errors0
DO Wall Time P99955ms
D1 Read Queries1,248,445
D1 Write Queries81,019

Excellent: Best results across all metrics. DO P99 under 1 second.

Step-by-Step Latency (150 LPS)

StepCountAvgP50P95P99Max
AuthorizeInit21,937106ms103ms129ms173ms1,102ms
EmailCodeGenerate21,937217ms210ms279ms331ms1,202ms
EmailCodeVerify21,937260ms252ms336ms400ms2,627ms
AuthorizeCode21,93768ms63ms88ms104ms8,351ms
Full Flow21,937652ms639ms756ms853ms8,871ms

Sharding Analysis

Cold Start vs Load Relationship

LPSLoad per ShardDO P99Status
501.56 req/s2,846ms⚠️ Cold starts frequent
1003.13 req/s1,393ms✅ Stable
1253.91 req/s1,153ms✅ Stable
1504.69 req/s955ms✅ Optimal

Finding: Each shard needs 3+ req/s to remain warm and perform optimally.

32 Shard Optimization Effect

MetricDec 13 (16 shards)Dec 17 (32 shards)
K6 P953,015ms283ms
Worker P99365ms69ms
DO P991,648ms955ms
DO Errors20
Success Rate99.97%100%

Capacity Recommendations

Load LevelLPSMonthly LoginsExpected Service Scale
Low~50~130MSmall/Medium (use 16 shards)
Normal~100~260MMedium services
Recommended Max~150~390MLarge services
Limit200+520M+Not tested

User Scale Conversion

1 Login = 5 HTTP requests:

  • 150 LPS × 5 = 750 RPS (HTTP request equivalent)
  • 390M monthly logins = ~19.5M monthly active users (weekly login)
  • 390M monthly logins = ~13M monthly active users (3 logins/month)

Key Findings

1. 32 Shards Eliminated All Issues

  • DO Errors: 443 → 0 (100% fix)
  • P95: 3,015ms → 283ms (91% reduction)
  • Spikes: Present (30s+) → None

2. Higher Load = Better DO Performance

Counterintuitively, DO P99 improves as load increases:

  • 50 LPS: 2,846ms (cold starts)
  • 150 LPS: 955ms (warm shards)

3. Worker Remains Efficient

Worker P99 stays under 100ms even at 150 LPS.

4. EmailCodeVerify is the Slowest Step

At 260ms average (40% of total flow time), this step involves:

  • OTP verification
  • Session creation
  • Multiple DO operations

Performance Visualization

DO Wall Time P99 by Load

xychart-beta
    title "DO Wall Time P99 (ms) - Improves with Higher Load"
    x-axis ["50 LPS", "100 LPS", "125 LPS", "150 LPS"]
    y-axis "Latency (ms)" 0 --> 3000
    bar [2846, 1393, 1153, 955]
LPSDO P99StatusNote
502,846ms⚠️Cold start frequent
1001,393msStable
1251,153msStable
150955msOptimal

Counter-intuitive finding: DO Wall Time improves as load increases due to reduced cold starts.

Bottleneck Analysis

Layer50 LPS100 LPS125 LPS150 LPS
Worker P9947ms67ms88ms69ms
DO P992,846ms ⚠️1,393ms1,153ms955ms ✅
K6 P95688ms696ms761ms756ms
VerdictCold startGoodGoodOptimal

Infrastructure Architecture

flowchart TB
    subgraph Test["Test Environment"]
        k6["k6 Cloud (Portland)"]
    end

    subgraph CF["Cloudflare Edge"]
        subgraph Workers["Workers"]
            OA["op-auth Worker
(66K req)"] OT["op-token Worker"] OM["op-mgmt Worker
(22K req)"] end subgraph DO["Durable Objects (shared)"] SS["SessionStore
(32 shards)"] ACS["AuthCodeStore
(32 shards)"] CS["ChallengeStore
(32 shards)"] end subgraph DB["Database"] D1["D1 Database (conformance)"] end end k6 -->|HTTPS| OA k6 -->|HTTPS| OT k6 -->|HTTPS| OM OA --> SS OA --> ACS OA --> CS OT --> SS OM --> SS SS --> D1 ACS --> D1 CS --> D1

Remaining Issues

1. Cold Start at Low Load

At 50 LPS, each shard receives only 1.56 req/s, causing cold starts.

Mitigation:

  • Use 16 shards for low load environments
  • Extend warmup period

2. DO P99 Variability

P99 ranges from 955ms to 2,846ms depending on load.

Mitigation:

  • Choose shard count based on expected load
  • Dynamic shard adjustment (future)

Test Execution Details

K6 Cloud Run URLs

TestURL
50 LPShttps://authrim.grafana.net/a/k6-app/runs/6338621
100 LPShttps://authrim.grafana.net/a/k6-app/runs/6338674
125 LPShttps://authrim.grafana.net/a/k6-app/runs/6338743
150 LPShttps://authrim.grafana.net/a/k6-app/runs/6338820

Conclusion

With 32-shard optimization, the Full Login Flow achieves:

  • 150 LPS (390M monthly logins) with stable performance
  • 100% success rate at all tested levels
  • DO errors eliminated (443 → 0)
  • P95 reduced by 91% (3,015ms → 283ms)

Key Improvements:

  • DO Errors: 443 → 0 (100% fix)
  • P95 Latency: 3,015ms → 283ms (91% reduction)
  • DO Wall Time P99: 1,648ms → 955ms (42% reduction)

Cold start at low load (50 LPS) is a known characteristic and can be mitigated by adjusting shard count based on expected traffic.