diff --git a/go/benchmarks/README.md b/go/benchmarks/README.md new file mode 100644 index 0000000000..24568d5a8a --- /dev/null +++ b/go/benchmarks/README.md @@ -0,0 +1,48 @@ +# API Benchmark Tools + +This directory contains k6 load testing scripts for comparing v1 and v2 Unkey APIs. + +## Scripts + +### ratelimit.js - Ratelimit API Benchmark +Tests the ratelimiting endpoints with random identifiers. + +```bash +UNKEY_ROOT_KEY=your_key REGION=us-east k6 cloud ratelimit.js +``` + +**Features:** +- 50/50 split between v1 (`api.unkey.dev`) and v2 (`api.unkey.com`) APIs +- Random user identifiers from pool of 5 users +- Tests `/v1/ratelimits.limit` vs `/v2/ratelimit.limit` + +### keyverify.js - Key Verification Benchmark +Tests the key verification endpoints. + +```bash +UNKEY_ROOT_KEY=your_key KEY=your_test_key k6 cloud keyverify.js +``` + +**Features:** +- 50/50 split between v1 and v2 APIs +- Tests `/v1/keys.verifyKey` vs `/v2/keys.verifyKey` + +## Setup +1. Install k6: `brew install k6` +2. Set up k6 Cloud account and configure project ID in scripts +3. Configure environment variables + +## Environment Variables +- `UNKEY_ROOT_KEY` - Required. Your Unkey root API key +- `KEY` - Required for keyverify.js. Test key to verify + +## Configuration +- **Duration:** 10 minutes at 10 req/s +- **Load zones:** Currently US East only (others commented out) +- **Thresholds:** P95 < 500ms, 99% success rate +- **Project ID:** 3788521 (update as needed) + +## Metrics +- `request_latency` - Custom trend metric with URL and region tags +- Built-in k6 HTTP metrics (duration, success rate, etc.) +- All metrics automatically available in Grafana Cloud diff --git a/go/benchmarks/keyverify.js b/go/benchmarks/keyverify.js new file mode 100644 index 0000000000..e6e946df46 --- /dev/null +++ b/go/benchmarks/keyverify.js @@ -0,0 +1,86 @@ +import http from 'k6/http'; +import { check } from 'k6'; +import { Rate, Trend } from 'k6/metrics'; + + +// Custom metrics +const requestLatencyTrend = new Trend('request_latency', true); + +const loadZones = [ + 'amazon:us:ashburn', // US East + // 'amazon:us:portland', // US West + // 'amazon:ie:dublin', // Europe West + // 'amazon:de:frankfurt', // Europe Central + // 'amazon:sg:singapore', // Asia Pacific + // 'amazon:jp:tokyo', // Asia Pacific East + // 'amazon:au:sydney', // Australia + // 'amazon:br:sao paulo', // South America + // 'amazon:in:mumbai', // India + // 'amazon:ca:montreal' // Canada +]; + +const equalPercent = Math.floor(100 / loadZones.length); +const distribution = {}; +loadZones.forEach((zone, index) => { + distribution[zone] = { + loadZone: zone, + percent: index === loadZones.length - 1 ? 100 - (equalPercent * (loadZones.length - 1)) : equalPercent + }; +}); + +export const options = { + cloud: { + project: "3788521", + distribution: distribution + }, + stages: [ + { duration: '10m', target: 10 }, // 10 req/s for 1 minute + ], + thresholds: { + http_req_duration: ['p(95)<500'], // 95% of requests must complete below 500ms + checks: ['rate>0.99'], // 99% of checks must pass + }, +}; + +const UNKEY_ROOT_KEY = __ENV.UNKEY_ROOT_KEY; +const KEY = __ENV.KEY; + +if (!UNKEY_ROOT_KEY) { + throw new Error('UNKEY_ROOT_KEY environment variable is required'); +} + +if (!KEY) { + throw new Error('KEY environment variable is required'); +} + +const headers = { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${UNKEY_ROOT_KEY}`, +}; + + + +export default function () { + + + + const response = Math.random() < 0.5 + ? http.post('https://api.unkey.dev/v1/keys.verifyKey', JSON.stringify({ + key: KEY, + }), { + headers: headers, + tags: { version: 'v1' }, + }) + : http.post('https://api.unkey.com/v2/keys.verifyKey', JSON.stringify({ + key: KEY + }), { + headers: headers, + tags: { version: 'v2' }, + }); + + check(response, { + 'status is 200': (r) => r.status === 200, + }); + + requestLatencyTrend.add(response.timings.duration, { url: response.request.url }); +} diff --git a/go/benchmarks/ratelimit.js b/go/benchmarks/ratelimit.js new file mode 100644 index 0000000000..b0b4f2bb18 --- /dev/null +++ b/go/benchmarks/ratelimit.js @@ -0,0 +1,94 @@ +import http from 'k6/http'; +import { check } from 'k6'; +import { Rate, Trend } from 'k6/metrics'; + + +// Custom metrics +const requestLatencyTrend = new Trend('request_latency', true); + +const loadZones = [ + 'amazon:us:ashburn', // US East + // 'amazon:us:portland', // US West + // 'amazon:ie:dublin', // Europe West + // 'amazon:de:frankfurt', // Europe Central + // 'amazon:sg:singapore', // Asia Pacific + // 'amazon:jp:tokyo', // Asia Pacific East + // 'amazon:au:sydney', // Australia + // 'amazon:br:sao paulo', // South America + // 'amazon:in:mumbai', // India + // 'amazon:ca:montreal' // Canada +]; + +const equalPercent = Math.floor(100 / loadZones.length); +const distribution = {}; +loadZones.forEach((zone, index) => { + distribution[zone] = { + loadZone: zone, + percent: index === loadZones.length - 1 ? 100 - (equalPercent * (loadZones.length - 1)) : equalPercent + }; +}); + +export const options = { + cloud: { + project: "3788521", + distribution: distribution + }, + stages: [ + { duration: '10m', target: 10 }, // 10 req/s for 1 minute + ], + thresholds: { + http_req_duration: ['p(95)<500'], // 95% of requests must complete below 500ms + checks: ['rate>0.99'], // 99% of checks must pass + }, +}; + +const UNKEY_ROOT_KEY = __ENV.UNKEY_ROOT_KEY; +const REGION = __ENV.REGION || 'local'; + +if (!UNKEY_ROOT_KEY) { + throw new Error('UNKEY_ROOT_KEY environment variable is required'); +} + +const headers = { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${UNKEY_ROOT_KEY}`, +}; + + + +const identifiers = ['user1', 'user2', 'user3', 'user4', 'user5'] +export default function () { + // Randomly choose between v1 and v2 (50/50 split) + + const identifier = identifiers[Math.floor(Math.random() * identifiers.length)]; + + + + + + const response = Math.random() < 0.5 + ? http.post('https://api.unkey.dev/v1/ratelimits.limit', JSON.stringify({ + namespace: 'benchmark', + identifier, + limit: 100, + duration: 60000 + }), { + headers: headers, + tags: { version: 'v1', region: REGION }, + }) + : http.post('https://api.unkey.com/v2/ratelimit.limit', JSON.stringify({ + namespace: 'benchmark', + identifier, + limit: 100, + duration: 60000 + }), { + headers: headers, + tags: { version: 'v2', region: REGION }, + }); + + check(response, { + 'status is 200': (r) => r.status === 200, + }); + + requestLatencyTrend.add(response.timings.duration, { url: response.request.url, region: REGION }); +}