95 lines
3.1 KiB
YAML
95 lines
3.1 KiB
YAML
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: k6-step-script
|
|
namespace: loadtest
|
|
data:
|
|
step.js: |
|
|
// Step-load (staircase) test. Hits a collection of endpoints, stepping
|
|
// the request rate up over time, tagging every request with its step and
|
|
// endpoint so latency can be sliced either way in Grafana.
|
|
//
|
|
// Env:
|
|
// BASE_URL - target base URL
|
|
// ENDPOINTS_JSON - [{"name":"list","method":"GET","path":"/x","weight":2}, ...]
|
|
// START_RPS - rate of step 1
|
|
// STEP_RPS - rate added each step
|
|
// STEP_DURATION - hold time per step, e.g. "2m"
|
|
// STEPS - number of steps
|
|
import http from 'k6/http';
|
|
import { check } from 'k6';
|
|
import exec from 'k6/execution';
|
|
|
|
const BASE = __ENV.BASE_URL;
|
|
let endpoints;
|
|
try {
|
|
endpoints = JSON.parse(__ENV.ENDPOINTS_JSON);
|
|
} catch (e) {
|
|
throw new Error('ENDPOINTS_JSON is not valid JSON: ' + e.message);
|
|
}
|
|
// expand weights
|
|
const pool = [];
|
|
for (const ep of endpoints) {
|
|
for (let i = 0; i < (ep.weight || 1); i++) pool.push(ep);
|
|
}
|
|
|
|
const startRps = parseInt(__ENV.START_RPS || '50');
|
|
const stepRps = parseInt(__ENV.STEP_RPS || '50');
|
|
const stepDurMs = ms(__ENV.STEP_DURATION || '2m');
|
|
const steps = parseInt(__ENV.STEPS || '5');
|
|
const rampMs = 5000;
|
|
|
|
// Build stage list and per-step [startMs, endMs, label] windows.
|
|
// Each step: 5s quick ramp to the rate, then a hold.
|
|
const stages = [];
|
|
const windows = [];
|
|
let clock = 0;
|
|
for (let i = 0; i < steps; i++) {
|
|
const rate = startRps + i * stepRps;
|
|
stages.push({ duration: ms2str(rampMs), target: rate });
|
|
clock += rampMs;
|
|
const wStart = clock;
|
|
stages.push({ duration: ms2str(stepDurMs), target: rate });
|
|
clock += stepDurMs;
|
|
windows.push([wStart, clock, `step${i + 1} @ ${rate} rps`]);
|
|
}
|
|
const totalMs = clock;
|
|
const maxRate = startRps + (steps - 1) * stepRps;
|
|
const vus = Math.max(10, Math.ceil(maxRate * 2));
|
|
|
|
function ms(s) {
|
|
const m = /^(\d+)(ms|s|m)$/.exec(String(s));
|
|
if (!m) throw new Error('bad duration: ' + s);
|
|
return +m[1] * ({ ms: 1, s: 1000, m: 60000 })[m[2]];
|
|
}
|
|
function ms2str(n) { return n + 'ms'; }
|
|
|
|
export const options = {
|
|
discardResponseBodies: true,
|
|
scenarios: {
|
|
staircase: {
|
|
executor: 'ramping-arrival-rate',
|
|
startRate: 0,
|
|
preAllocatedVUs: vus,
|
|
maxVUs: vus,
|
|
stages: stages,
|
|
},
|
|
},
|
|
thresholds: { http_req_failed: ['rate<0.05'] },
|
|
};
|
|
|
|
export default function () {
|
|
const elapsed = exec.scenario.progress * totalMs;
|
|
let step = 'ramp-up';
|
|
for (const [s, e, label] of windows) {
|
|
if (elapsed >= s && elapsed < e) { step = label; break; }
|
|
}
|
|
const ep = pool[exec.scenario.iterationInTest % pool.length];
|
|
const url = BASE + ep.path;
|
|
const params = { tags: { name: ep.name, step: step }, timeout: '15s' };
|
|
const res = (ep.method || 'GET') === 'POST'
|
|
? http.post(url, ep.body || '', params)
|
|
: http.get(url, params);
|
|
check(res, { ok: (r) => r.status < 300 });
|
|
}
|