| Item | Details |
|---|
| Test Date | December 2025 |
| Target Endpoint | POST /token (grant_type=refresh_token) |
| Purpose | Measure refresh token rotation performance with theft detection |
| Component | Technology | Description |
|---|
| Worker | Cloudflare Workers | OAuth2/OIDC endpoint processing |
| Durable Objects | Cloudflare DO | RefreshTokenRotator (token rotation management) |
| Database | Cloudflare D1 | User info, sessions, audit logs |
| Cache | Cloudflare KV | JWK cache, RBAC claim cache, user cache |
flowchart TB
subgraph Local["Test Environment"]
k6["k6 OSS / K6 Cloud"]
end
subgraph CF["Cloudflare"]
Edge["Cloudflare Edge"]
subgraph Worker["ar-token Worker"]
W["Token Processing + JWT Signing"]
end
subgraph Cache["Cache Layer"]
KV["KV: JWK / RBAC / User Cache"]
end
subgraph DO["Durable Objects"]
RTR["RefreshTokenRotator"]
end
subgraph DB["Database"]
D1["D1: sessions / audit_logs"]
end
end
k6 -->|HTTPS| Edge
Edge --> W
W --> KV
W --> RTR
W --> D1
RTR --> D1
| Setting | Value | Production Recommended |
|---|
REFRESH_TOKEN_ROTATION_ENABLED | true | true |
REFRESH_TOKEN_EXPIRY | 30 days | 30 days |
ACCESS_TOKEN_EXPIRY | 1 hour | 1 hour |
RBAC_CACHE_TTL | 5 min | 5 min |
USER_CACHE_TTL | 1 hour | 1 hour |
Simulates production refresh token rotation behavior:
| Item | Setting |
|---|
| Token Rotation | Enabled (new refresh token issued each time) |
| VU Design | Independent token family per VU |
| Test Pattern | Normal rotation path only (no error cases) |
| Think Time | 0ms (continuous requests) |
Version-based theft detection:
"client_id": "client_id",
"rtv": 5, // Refresh Token Version
- rtv (Refresh Token Version): Version number within token family
- Old version token use → Invalidate all tokens as theft
- State management in Durable Objects, audit persistence in D1
| Preset | Target RPS | Duration | Max VU | Use Case |
|---|
| rps100 | 100 RPS | 2 min | 120 | Production baseline |
| rps200 | 200 RPS | 2 min | 240 | High traffic scenario |
| rps300 | 300 RPS | 2 min | 360 | Peak load validation |
Execution Date: December 3, 2025 09:33 JST
| Metric | Value |
|---|
| Total Requests | 29,186 |
| Success Rate | 100% |
| Token Rotation Success | 100% |
| Errors | 0 |
| Metric | Value | Notes |
|---|
| Worker Duration P50 | 9.35 ms | Median |
| Worker Duration P75 | 10.44 ms | |
| Worker Duration P90 | 39.30 ms | |
| Worker Duration P99 | 816.24 ms | Tail latency |
| CPU Time P50 | 4.80 ms | |
| CPU Time P99 | 14.40 ms | |
| DO Wall Time P50 | 9.16 ms | Durable Objects processing |
| DO Wall Time P99 | 18.43 ms | |
| D1 Reads | 10,510 | 0.36/request |
| D1 Writes | 23,518 | 0.81/request |
| Metric | Calculated Value | Description |
|---|
| DO Requests/Worker Request | 3.09 | Subrequest efficiency |
| D1 Reads/Request | 0.36 | RBAC cache hit rate > 95% |
| D1 Writes/Request | 0.81 | Audit log and session updates |
| Shards | DO P99 | DO Errors | HTTP Failures |
|---|
| 32 | 781ms | 11,972 | Many |
| 48 | 43ms | 0 | 0 |
Dramatic improvement:
- DO P99: 781ms → 43ms (95% reduction)
- DO Errors: 11,972 → 0 (100% elimination)
| Metric | 32 Shards | 48 Shards | Improvement |
|---|
| Worker P50 | 12ms | 12ms | Same |
| Worker P95 | 100ms | 39ms | -61% |
| Worker P99 | 781ms | 43ms | -94% |
| DO Errors | 11,972 | 0 | -100% |
| Success Rate | ~96% | 100% | ✅ |
| Optimization Stage | D1 Reads/Request | Improvement |
|---|
| V1 (no cache) | ~14.6 | Baseline |
| V2 (before RBAC cache) | 9.7 | -34% |
| V2 (after RBAC cache) | 0.36 | -96% |
| Date | Optimization | Effect |
|---|
| 2025-12-01 | TokenFamilyV2 (version-based theft detection) | Reduced DO storage I/O |
| 2025-12-01 | UserCache (KV Read-Through) | Reduced D1 user queries |
| 2025-12-03 | Async audit logging (Fire-and-Forget) | Reduced response latency |
| 2025-12-03 | RBAC claim cache (5-min TTL) | 96% reduction in D1 RBAC queries |
| Cache | TTL | Purpose |
|---|
| USER_CACHE | 1 hour | User information (Read-Through) |
| REBAC_CACHE | 5 min | RBAC claims (roles, permissions, groups) |
| CLIENTS_CACHE | 1 hour | Client information |
| KeyManager | 5 min | JWK signing keys (Worker memory) |
| Usage | Recommended RPS | Rationale |
|---|
| Normal Operation | ≤200 | P99 < 500ms maintained |
| Peak Handling | ≤2,500 | With 48 shards |
| Absolute Limit | ≤3,000 | Zero errors with 48 shards |
| RPS | Token Issuance/Hour | Token Issuance/Day | Estimated MAU |
|---|
| 100 | 360,000 | 8.6M | 200K-400K |
| 200 | 720,000 | 17.3M | 500K-1M |
| 300 | 1,080,000 | 25.9M | 1M-2M |
Conversion Formula:
RPS = (MAU × DAU rate × Requests/DAU) / (Operating hours × 3600) × Peak coefficient
48 shards completely eliminated errors at 3,000 RPS while 32 shards had 11,972 DO errors.
D1 reads dropped from 14.6/request to 0.36/request.
100% success rate at 200 RPS with rotation enabled.
CPU time remains at 4-15ms even under load.
| Metric | Target | Result | Status |
|---|
| Success Rate | > 99.9% | 100% | ✅ |
| Token Rotation | > 99% | 100% | ✅ |
| Worker Duration P99 | < 1000ms | 816ms / 43ms | ✅ |
| DO Wall Time P99 | < 100ms | 18.43ms / 43ms | ✅ |
| D1 Reads/Request | < 5 | 0.36 | ✅ |
- 200 RPS with P99 < 500ms
- Suitable for 500K-1M MAU
- 300-400 RPS (current architecture limit)
- 2,500-3,000 RPS with 48 shards (production tested)
- DO Sharding Extension: Shard RefreshTokenRotator by client_id + user_id
- D1 Read Replicas: Read optimization for global deployment
- Cloudflare Queues: Async batch processing for audit logs
Authrim’s Refresh Token endpoint with rotation achieves:
- 100% success rate at 200 RPS
- 100% token rotation success
- 0.36 D1 reads per request (96% reduction via caching)
- Zero errors at 3,000 RPS with 48 shards
Key Takeaway: Proper sharding (48 shards) transforms the system from unstable (11,972 errors) to perfectly reliable (0 errors) at 3,000 RPS.
TokenFamilyV2 design provides both security (theft detection) and performance (version-based state management).