// k6 load-test: tenant-scoped read path (`GET /v1/entities/:slug/rows`). // // Profile: 100 VUs for 60s. The read path is the hottest API surface in // production, so the p(99) latency budget is tight and a minimum throughput // floor is asserted to catch regressions that pass percentile checks while // quietly halving RPS (e.g. accidental serialisation under contention). // // Run: // K6_TARGET_URL=http://localhost:3000 \ // LOAD_TENANT_API_KEY=tnk_live_xxx \ // k6 run tests/load/api-read.js // // Required env: // LOAD_TENANT_API_KEY tenant-scoped API key for the test tenant // Optional env: // LOAD_ENTITY_SLUG entity slug to read (default: load-test-entity) import http from "k6/http"; import { check, group } from "k6"; export const options = { scenarios: { api_read: { executor: "constant-vus", vus: 100, duration: "60s", tags: { suite: "api-read" }, }, }, thresholds: { http_req_failed: ["rate<0.01"], http_req_duration: ["p(99)<300"], http_reqs: ["rate>200"], }, discardResponseBodies: true, }; const TARGET_URL = __ENV.K6_TARGET_URL || "http://localhost:3000"; const TENANT_API_KEY = __ENV.LOAD_TENANT_API_KEY || "tnk_load_test_key"; const ENTITY_SLUG = __ENV.LOAD_ENTITY_SLUG || "load-test-entity"; export default function apiReadScenario() { group("api-read", function () { const params = { headers: { accept: "application/json", authorization: `Bearer ${TENANT_API_KEY}`, }, tags: { name: "api-read" }, }; const res = http.get(`${TARGET_URL}/v1/entities/${ENTITY_SLUG}/rows`, params); check(res, { "status is 200 or 401 or 404": (r) => r.status === 200 || r.status === 401 || r.status === 404, }); }); }