- Home
- Skills
- Testing & Debugging
- Stress Testing — Load, Spike, Soak & Breakpoint Plans
Works with the AI Agents you already use
Stress Testing — Load, Spike, Soak & Breakpoint Plans
Plans and interprets load tests (smoke, stress, spike, soak, breakpoint) with realistic workloads and SLO thresholds.
Free
Stress Testing — Load, Spike, Soak & Breakpoint Plans
Example session with this skill installed
Please plan a load test for our checkout API before Black Friday. We use k6.
- Target: staging, https://staging-api.shopfictional.dev
- Endpoints:
GET /products?page=N,GET /products/:id,POST /cart/items,POST /checkout - From our APM: normal peak hour is about 120 requests/second across these endpoints. We expect roughly 2x on Black Friday.
- SLO: p95 under 400 ms, error rate under 1%.
Don't run anything yet. I want the plan and the script first.
- Read your context and instructions
- Compiled the stress testing
Summary
Nothing has been run. This is the plan, the k6 script, and the pass/fail criteria for you to review. The goal is to answer one question: does checkout hold the Black Friday peak on staging? Given your numbers, that means a
smoke run, an
average-load baseline at 120 req/s, and a
stress run at 240 req/s, in that order. A
spike run is optional; it needs a spike level from you. All load uses the
open model (arrival rate), because web traffic keeps arriving whether or not the API is slow.
Confirm before any run
Target and environment: https://staging-api.shopfictional.dev. Is staging shared with other teams tonight? A stress run at 240 req/s will degrade it for anyone else using it.
2. Payments: POST /checkout must hit a payment sandbox, not a real provider. Please confirm.
3.
Data volume: how close are staging's product, user, and order tables to production size? Problems that hide at 1k rows often show up at 1M. This plan writes down whatever gap exists as a limit on the conclusions.
4.
Traffic mix: I assumed 60% browse list / 25% product detail / 10% add to cart / 5% checkout. That is a placeholder. Replace it with the endpoint split from your APM.
5.
Spike level: if you want a spike test, give me the surge you're planning for (for example, the expected traffic when the campaign email goes out).
6.
Load generator: run it from a machine with headroom, in the same location for every run, and keep it the same for future baseline comparisons.
Plan
| Run | Question | Profile | Pass/fail |
|---|---|---|---|
| Smoke | Do the script and the environment work? | 2 VUs, 1 min | All checks pass, 0 errors |
| Average-load | Does it handle a normal peak hour? | Ramp to 120 req/s over 5 min, hold 30 min, ramp down 5 min | p95 < 400 ms, errors < 1% |
| Stress | Does it handle 2x? | Ramp to 240 req/s over 10 min, hold 20 min, ramp down 5 min | Same thresholds |
| Spike (optional) | Does it survive a surge, and recover? | 120 → SPIKE_RPS in 30 s, hold 2 min, back to 120 | Same thresholds, plus a return to baseline latency within minutes |
Stress runs only if average-load passes. If the system fails at normal load, a 2x test produces noise.
Script: checkout-load.js
import http from 'k6/http';
import { check } from 'k6';
import { SharedArray } from 'k6/data';
const BASE = __ENV.BASE_URL;
const PROFILE = __ENV.PROFILE || 'smoke';
// Parameterized data: varied products and users, so caches don't fake the result.
const products = new SharedArray('products', () => JSON.parse(open('./data/products.json')));
const users = new SharedArray('users', () => JSON.parse(open('./data/users.json'))); // staging test accounts + tokens
const arrival = (stages) => ({
executor: 'ramping-arrival-rate', // open model
startRate: 0,
timeUnit: '1s',
preAllocatedVUs: 200,
maxVUs: 1000,
stages,
});
const PROFILES = {
smoke: { executor: 'constant-vus', vus: 2, duration: '1m' },
average: arrival([
{ target: 120, duration: '5m' },
{ target: 120, duration: '30m' },
{ target: 0, duration: '5m' },
]),
stress: arrival([
{ target: 240, duration: '10m' },
{ target: 240, duration: '20m' },
{ target: 0, duration: '5m' },
]),
spike: arrival([
{ target: 120, duration: '2m' },
{ target: Number(__ENV.SPIKE_RPS), duration: '30s' },
{ target: Number(__ENV.SPIKE_RPS), duration: '2m' },
{ target: 120, duration: '30s' },
{ target: 120, duration: '5m' }, // recovery window
]),
};
export const options = {
scenarios: { [PROFILE]: PROFILES[PROFILE] },
thresholds: {
// SLO, measured on successful responses so fast errors can't flatter latency
'http_req_duration{expected_response:true}': ['p(95)<400'],
http_req_failed: ['rate<0.01'],
// per-flow visibility: checkout must meet the SLO on its own
'http_req_duration{flow:checkout}': ['p(95)<400'],
// generator health: dropped iterations mean the generator couldn't keep the rate
dropped_iterations: ['count<1'],
},
};
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
export default function () {
const r = Math.random();
const user = pick(users);
const auth = { headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' } };
if (r < 0.60) {
const page = 1 + Math.floor(Math.random() * 20);
const res = http.get(`${BASE}/products?page=${page}`, { tags: { name: 'GET /products', flow: 'browse' } });
check(res, { 'list 200': (x) => x.status === 200 });
} else if (r < 0.85) {
const res = http.get(`${BASE}/products/${pick(products).id}`, { tags: { name: 'GET /products/:id', flow: 'detail' } });
check(res, { 'detail 200': (x) => x.status === 200 });
} else if (r < 0.95) {
const res = http.post(`${BASE}/cart/items`, JSON.stringify({ productId: pick(products).id, qty: 1 }),
{ ...auth, tags: { name: 'POST /cart/items', flow: 'cart' } });
check(res, { 'cart 2xx': (x) => x.status >= 200 && x.status < 300 });
} else {
const add = http.post(`${BASE}/cart/items`, JSON.stringify({ productId: pick(products).id, qty: 1 }),
{ ...auth, tags: { name: 'POST /cart/items', flow: 'checkout' } });
// Don't fire a dependent request after a failure; under load, errors are expected.
if (!check(add, { 'checkout: cart 2xx': (x) => x.status >= 200 && x.status < 300 })) return;
const res = http.post(`${BASE}/checkout`, JSON.stringify({ paymentMethod: 'sandbox_test_card' }),
{ ...auth, tags: { name: 'POST /checkout', flow: 'checkout' } });
check(res, { 'checkout 2xx': (x) => x.status >= 200 && x.status < 300 });
}
}
Each iteration is one request, except checkout, which makes two. So the configured arrival rate approximates requests per second, and runs slightly above it because of the checkout share. data/products.json and data/users.json must hold real staging IDs and test-account tokens.
Commands (not executed)
k6 run -e BASE_URL=https://staging-api.shopfictional.dev -e PROFILE=smoke checkout-load.js
k6 run -e BASE_URL=https://staging-api.shopfictional.dev -e PROFILE=average checkout-load.js
k6 run -e BASE_URL=https://staging-api.shopfictional.dev -e PROFILE=stress checkout-load.js
k6 run -e BASE_URL=https://staging-api.shopfictional.dev -e PROFILE=spike -e SPIKE_RPS=<agreed> checkout-load.js
k6 exits non-zero when a threshold fails, so the same commands work as a CI gate. Keep a kill switch ready (Ctrl+C, or stopping the generator host) for every run beyond smoke.
How to read the results
Report each threshold next to its measured value (for example, "p95 X ms vs 400 ms"), never "seemed fine". Paste the k6 summary, trimmed to the relevant lines.
Read p50/p90/p95/p99 and the max, not the average. A p50–p99 gap that widens as load climbs points to queuing: pools, locks, GC, or a slow downstream service.
Read errors and latency together. Thresholds are measured on expected_response:true because a 5 ms 500 would otherwise make latency look better.
Watch for a throughput plateau. If req/s stays flat while the configured rate keeps rising, the system has saturated. Find the timestamp and check CPU, memory, DB connections in use, queue depth, and payment-sandbox latency. The first resource to saturate is the finding.
Classify the failure mode: graceful (429s, shedding, queuing with backpressure) or catastrophic (timeout cascades, OOM, restarts). Catastrophic failure at 240 req/s is a fix before Black Friday, not a note.
Check generator health. If dropped_iterations is above zero or the generator's CPU or network was maxed, the run is inconclusive. Discard it and rerun with more capacity; don't report its numbers.
Measure recovery after stress and spike: latency should return to baseline and queues should drain within minutes.
Next steps
- Answer the six confirmation questions above, especially the real traffic mix and whether staging is shared.
- Fill
data/products.jsonanddata/users.jsonfrom staging. - Run smoke and share the output; I'll check the script's behavior before any heavy run.
- After average-load, record the baseline (thresholds, measured percentiles, environment, generator location) in
learnings/. That baseline is what the stress run, and every future run, gets compared against.
Connects securely to your tools. The creator never sees your data.
About this skill
Stress Testing helps you answer "how much traffic can this handle?" with evidence. There is no single "stress test": the skill distinguishes six test types, each answering one question. It then models realistic load and judges results by percentiles against an SLO declared before any load is generated.
The six types:
- Smoke: does it work at all?
- Average-load: does it handle normal traffic?
- Stress: does it handle the peak?
- Spike: does it survive a sudden surge?
- Breakpoint: where and how does it break?
- Soak: does it degrade over hours?
The order is fixed: smoke first, then average-load, and only then the heavier types.
Workflow: define pass/fail thresholds first (p95/p99 and error rate, never averages) → model the workload → confirm the target and environment with you → run smoke, then the chosen type, with observability on → interpret → record the baseline.
Inside:
references/test-types.md: the load profile and use case for each of the six types.references/workload-modeling.md: the open model (arrival rate) versus the closed model (VUs), the coordinated-omission trap, traffic mix from real data, parameterized test data, and generator discipline.references/interpreting-results.md: thresholds versus measured values, reading the latency distribution, finding the knee and the first resource to saturate, graceful versus catastrophic failure, recovery, and a report template.
Safety by design: the skill activates only on an explicit request. It never runs or suggests a load test on its own, never targets production unless you direct it and own that decision, and discards any run where the load generator itself saturated.
Free and MIT-licensed, part of claude-skills-czl.
Source on GitHub: this skill · full repository
How to use
Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill passes our trust checks below.
Security scanned
Passed our 8-point scan before listing
Fresh listing
Recently published to Agensi
Free forever
No account required to browse
Trust & safety
Security scanned
Verified clean 11 days ago
- Free to download with an account