# k6 Load Testing Reproducible load test profile for `POST /api/v1/decide`. ## Prerequisites - Docker + Docker Compose - `jq` - Running stack (`docker compose -f compose.yaml up -d`) ## One-command run ```bash ./infrastructure/k6/run-decide.sh ``` This command: 1. Prepares deterministic fixture via `prepare_k6_fixture`. 2. Runs `grafana/k6` in a pinned container image. 3. Saves artifacts to `artifacts/k6//`. Artifacts: - `fixture.json` - `run.env` - `summary.json` ## Reproducible rerun Use the same `RUN_ID` and k6 profile parameters. ```bash RUN_ID=baseline_20260224 \ START_RPS=20 \ RAMP_UP_RPS=200 \ HOLD_RPS=200 \ HOLD_DURATION=2m \ ./infrastructure/k6/run-decide.sh ``` ## Target URL Default target for k6 container: - `K6_BASE_URL=http://host.docker.internal` Override if needed: ```bash K6_BASE_URL=http://host.docker.internal:14609 ./infrastructure/k6/run-decide.sh ``` ## Profile knobs - `START_RPS` - `RAMP_UP_RPS` - `HOLD_RPS` - `RAMP_UP_DURATION` - `HOLD_DURATION` - `RAMP_DOWN_DURATION` - `PRE_ALLOCATED_VUS` - `MAX_VUS` - `THRESHOLD_ERROR_RATE` - `THRESHOLD_P95_MS` - `THRESHOLD_P99_MS` - `K6_IMAGE` ## Compare two runs ```bash BASE=artifacts/k6/baseline_20260224/summary.json CAND=artifacts/k6/candidate_20260224/summary.json jq -n --argfile b "$BASE" --argfile c "$CAND" '{ baseline_p95_ms: $b.metrics.http_req_duration["p(95)"], candidate_p95_ms: $c.metrics.http_req_duration["p(95)"], baseline_req_per_s: $b.metrics.http_reqs.rate, candidate_req_per_s: $c.metrics.http_reqs.rate, baseline_error_rate: $b.metrics.http_req_failed.value, candidate_error_rate: $c.metrics.http_req_failed.value }' ```