Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions go/benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -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
86 changes: 86 additions & 0 deletions go/benchmarks/keyverify.js
Original file line number Diff line number Diff line change
@@ -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 });
}
94 changes: 94 additions & 0 deletions go/benchmarks/ratelimit.js
Original file line number Diff line number Diff line change
@@ -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 });
}
Loading