DEV Community

Elvin Aghalarov
Elvin Aghalarov

Posted on Originally published at hoofbeat.dev

"Load, stress, spike, soak: four performance tests and the question each one answers"

"We load tested it" can mean four very different things. Each test answers a different question, and running the wrong one gives you a confident answer to a question you didn't ask.

Test Question Shape Length
Load Do we meet our goals at expected traffic? Ramp to peak, hold 10–30 min
Stress Where and how do we break? Keep increasing Until it fails
Spike Do we survive a sudden burst? Instant jump 5–15 min
Soak Do we stay healthy for hours? Moderate, constant 2–24 h

All examples below use k6's ramping-arrival-rate executor, so the load is new users per second, which is how real traffic arrives. Only the stages change.

// shared setup
import http from 'k6/http';
export default function () {
  http.get(`${__ENV.BASE_URL}/products`);
}
Enter fullscreen mode Exit fullscreen mode

1. Load test: "are we ready?"

Simulate the traffic you expect and check it against goals.

export const options = {
  scenarios: {
    load: {
      executor: 'ramping-arrival-rate',
      startRate: 0, timeUnit: '1s', preAllocatedVUs: 200, maxVUs: 2000,
      stages: [
        { target: 50, duration: '5m' },   // ramp to expected peak
        { target: 50, duration: '20m' },  // hold
        { target: 0, duration: '2m' },
      ],
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.01'],
  },
};
Enter fullscreen mode Exit fullscreen mode

Clear pass/fail: thresholds hold or they don't. Run it before every launch and on a schedule after.

2. Stress test: "where's the ceiling?"

Keep increasing past expected traffic until something gives.

stages: [
  { target: 50, duration: '3m' },
  { target: 100, duration: '3m' },
  { target: 200, duration: '3m' },
  { target: 400, duration: '3m' },
  { target: 0, duration: '3m' },   // watch the recovery!
],
Enter fullscreen mode Exit fullscreen mode

You learn two things:

  1. The breaking point: the load where p95 or errors cross your limits.
  2. The failure mode: graceful slowdown, clean 503s, or a cliff that takes minutes to recover from.

That last ramp-down stage matters: a system that doesn't recover on its own needs work, whatever its ceiling is.

3. Spike test: "what if we go viral?"

Real traffic doesn't ramp politely. Newsletters, Hacker News, a sale at 10:00 sharp.

stages: [
  { target: 10, duration: '2m' },    // normal
  { target: 300, duration: '10s' },  // spike
  { target: 300, duration: '5m' },   // hold
  { target: 10, duration: '1m' },
],
Enter fullscreen mode Exit fullscreen mode

Spikes find what gentle ramps never do:

  • autoscaling that needs 3 minutes while the spike takes 10 seconds
  • cold caches sending everything to the database at once
  • pools sized for the average
  • one hot row (the stock count of the sale item) everyone writes to

4. Soak test: "can we run all week?"

Moderate load, held for hours.

stages: [
  { target: 30, duration: '5m' },
  { target: 30, duration: '4h' },
  { target: 0, duration: '5m' },
],
Enter fullscreen mode Exit fullscreen mode

You're looking for drift: something that moves while the load doesn't.

  • memory that climbs and never plateaus → leak or unbounded cache
  • connections that slowly run out → resources not released on error paths
  • latency creeping up hour by hour → growing tables, fragmentation, GC pressure
  • errors at the same clock time → a cron job colliding with traffic

Compare the first hour's p95 with the last hour's. Healthy systems look boring.

Which one when?

  • Every pull request: a short, steady load test that catches regressions
  • Before every launch: load test at expected peak
  • Quarterly or after big changes: stress test to know your ceiling
  • Before known events: spike test shaped like the event
  • Before trusting a new service for weeks: soak test, overnight

TL;DR

Same user journey, four load shapes, four different questions. If you only ever ramp to peak and hold, you've answered "are we ready for a normal Tuesday?" and nothing else.


I build Hoofbeat, where all four are the same flow with a different load shape: schedule the soak overnight and get a Slack alert only if it regresses. Original post on the Hoofbeat blog.

Top comments (0)