/** * Load test: Progressive ramp - stream server HLS * Ramp: 10 -> 50 -> 100 -> 200 VUs to find saturation point * Usage: k6 run loadtests/stream/ramp.js * Requires: STREAM_ORIGIN, AUTH_TOKEN, TRACK_ID (see hls.js) */ import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; const errorRate = new Rate('ramp_errors'); const manifestDuration = new Trend('ramp_manifest_duration'); const STREAM_ORIGIN = __ENV.STREAM_ORIGIN || 'http://localhost:8082'; const AUTH_TOKEN = __ENV.AUTH_TOKEN || ''; const TRACK_ID = __ENV.TRACK_ID || ''; export const options = { scenarios: { ramp: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '1m', target: 10 }, { duration: '2m', target: 50 }, { duration: '2m', target: 100 }, { duration: '2m', target: 200 }, { duration: '1m', target: 0 }, ], gracefulRampDown: '30s', gracefulStop: '30s', }, }, thresholds: { http_req_duration: ['p(95)<2000', 'p(99)<5000'], ramp_errors: ['rate<0.05'], ramp_manifest_duration: ['p(95)<1000', 'p(99)<3000'], }, }; function getHeaders() { const h = { Accept: 'application/vnd.apple.mpegurl,*/*' }; if (AUTH_TOKEN) { h['Authorization'] = `Bearer ${AUTH_TOKEN}`; } return h; } export function setup() { return { trackId: TRACK_ID || '00000000-0000-0000-0000-000000000000' }; } export default function (data) { const { trackId } = data; const url = `${STREAM_ORIGIN}/hls/${trackId}/master.m3u8`; const start = Date.now(); const res = http.get(url, { headers: getHeaders() }); manifestDuration.add(Date.now() - start); errorRate.add(res.status !== 200 && res.status !== 404); sleep(0.5); }