Skip to main content
Sep 28, 2026ecommerce load testing

By Lucas Yoris · Performate

Black Friday Readiness: Capacity Planning and Load Test Scenarios That Matter

Model checkout bursts, inventory contention, and payment latency—not vanity homepage hits—for ecommerce peaks.

Your homepage load test passed at 2,000 req/s—and checkout still melted at 11:00 when coupon codes went live. Retail peaks punish write-heavy paths: cart mutations, inventory locks, payment callbacks, and fraud checks compete for the same database rows. Scripts that only hammer the marketing site tell leadership a comforting story while the revenue path remains untested.

Black Friday readiness is a mix problem: browse-to-buy ratios, authenticated sessions with warm carts, and third-party payment latency that sandbox tests often ignore. In this guide you will model parallel k6 scenarios that mirror real funnel percentages, tag metrics by journey stage, and define thresholds leadership actually cares about—not isolated search p95 on an anonymous catalog.

Why ecommerce peaks break different surfaces than marketing traffic

Homepage and CDN-cached assets scale horizontally. Checkout does not:

  • Inventory contention — two users grabbing the last SKU hit the same row; locks queue under spike traffic.
  • Cart state — authenticated writes stress session stores and API gateways differently than anonymous GETs.
  • Payment hops — PSP latency and webhook retries add seconds tail latency even when your API is "fast" (third-party APIs).
  • Promo bursts — coupon validation triggers extra rules engines and audit writes in a narrow window.

Think of peak traffic like a stadium exit: the concourse (browse) flows until everyone hits the same narrow gates (checkout) at once (stress vs load vs spike).

When search latency looks fine but revenue fails

Leadership reads checkout conversion, payment errors, and order duplication—not catalog search medians. Load tests must tag funnel stages and threshold the paths that map to revenue (how to read load test reports).

Practical k6 implementation: browse-to-buy mix with spike stage

Model browse, cart, and checkout as parallel scenarios with rates from last year's analytics—or merchandising forecasts if this is a new storefront. Add a short spike stage before the hold period to simulate doorbuster minutes.

Example script (illustrative—not production-ready). Fictional URLs, SKUs, and rates—adapt to your funnel.

What this example demonstrates:

  • Realistic traffic mix: browse 60 req/s, cart 25 req/s, checkout 15 req/s (~60/25/15 split).
  • Stage tags (journey:browse, journey:checkout) for per-funnel thresholds.
  • Spike executor ramping checkout to 40 req/s for five minutes mid-run.
  • Payment sandbox delay simulated via env-configured sleep after checkout POST.
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.API_BASE || 'https://staging-shop.example.com';
const TOKEN = __ENV.AUTH_TOKEN;

export const options = {
  scenarios: {
    browse: {
      executor: 'constant-arrival-rate',
      rate: 60,
      timeUnit: '1s',
      duration: '20m',
      preAllocatedVUs: 30,
      maxVUs: 120,
      tags: { journey: 'browse' },
      exec: 'browseCatalog',
    },
    cart_updates: {
      executor: 'constant-arrival-rate',
      rate: 25,
      timeUnit: '1s',
      duration: '20m',
      preAllocatedVUs: 15,
      maxVUs: 60,
      tags: { journey: 'cart' },
      exec: 'updateCart',
    },
    checkout_steady: {
      executor: 'constant-arrival-rate',
      rate: 15,
      timeUnit: '1s',
      duration: '20m',
      preAllocatedVUs: 20,
      maxVUs: 80,
      tags: { journey: 'checkout' },
      exec: 'checkoutFlow',
    },
    checkout_spike: {
      executor: 'ramping-arrival-rate',
      startRate: 15,
      timeUnit: '1s',
      stages: [
        { duration: '10m', target: 15 },
        { duration: '5m', target: 40 },  // doorbuster window
        { duration: '5m', target: 15 },
      ],
      preAllocatedVUs: 25,
      maxVUs: 100,
      tags: { journey: 'checkout', phase: 'spike' },
      exec: 'checkoutFlow',
      startTime: '0s',
    },
  },
  thresholds: {
    'http_req_duration{journey:checkout}': ['p(95)<1500', 'p(99)<2500'],
    'http_req_duration{journey:browse}': ['p(95)<600'],
    http_req_failed: ['rate<0.02'],
  },
};

export function browseCatalog() {
  const res = http.get(`${BASE}/v1/catalog?category=deals&page=1`, {
    tags: { journey: 'browse' },
  });
  check(res, { 'browse 2xx': (r) => r.status >= 200 && r.status < 300 });
  sleep(0.5);
}

export function updateCart() {
  const body = JSON.stringify({ sku: 'DEAL-42', qty: 1 });
  const res = http.post(`${BASE}/v1/cart/items`, body, {
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}` },
    tags: { journey: 'cart' },
  });
  check(res, { 'cart 2xx': (r) => r.status >= 200 && r.status < 300 });
  sleep(0.3);
}

export function checkoutFlow() {
  const body = JSON.stringify({ cartId: 'cart-demo', paymentMethod: 'sandbox' });
  const res = http.post(`${BASE}/v1/checkout`, body, {
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}` },
    tags: { journey: 'checkout' },
  });
  check(res, { 'checkout 2xx': (r) => r.status >= 200 && r.status < 300 });
  sleep(Number(__ENV.PSP_DELAY_SEC || 0.8)); // simulate PSP round-trip
}

Patterns that work

  • Warm authenticated sessions with realistic cart payloads—not empty POST bodies.
  • Spike stage on checkout only while browse holds steady—mirrors promo-minute behavior (holiday traffic patterns).
  • Separate thresholds per journey so green browse does not mask red checkout.
  • Declared test windows with ops on-call—undeclared load still triggers incidents (ethical testing).

Anti-patterns to avoid

  • Homepage-only scripts before peak season sign-off.
  • Ignoring payment sandbox slowdowns—production PSP latency dominates tails.
  • Running spike tests without inventory seed data—contention results will not reproduce.

Pro tip (example command):

k6 run black-friday-mix.js --summary-trend-stats="p(95),p(99)" -e PSP_DELAY_SEC=1.2

What this command demonstrates: tail stats per journey tag surface checkout regression during the five-minute spike stage.

Decision framework: scenario mix vs peak shape

SituationRecommended action
First Black Friday on new stackStart from forecast; validate browse/checkout split weekly
Repeat storefront with analyticsPull last year's funnel rates; add 20% headroom on checkout
Heavy promo / doorbuster SKUsDedicated spike scenario on checkout + inventory SKUs
PSP latency unknownMeasure sandbox p95; add env sleep to match (idempotency flows)
Staging smaller than prodScale rates down proportionally; document fidelity gap

Use steady parallel scenarios if you need a regression baseline across merchandising copy changes.

Add a spike stage if marketing confirms minute-level bursts (doorbusters, email drops).

Block peak sign-off if checkout p99 or http_req_failed violates thresholds during spike—even when browse stays green.

Observability, documentation, and next steps

Peak readiness is an ops rehearsal, not only a k6 exit code:

  • Document funnel percentages and data source (last BF analytics or forecast).
  • Assign abort contacts and queue depth monitors before long runs.
  • Correlate journey:checkout tags with payment provider dashboards during tests.
  • Track order duplication and idempotency failures as custom metrics if revenue risk is high.
  • Archive scenario JSON and git SHA per dress rehearsal (capacity planning from k6).

How Performate simplifies Black Friday load testing

Merchandising changes copy weekly; script forks do not survive November. Below is a concrete workflow for browse + cart + checkout scenarios—adapt rates to your funnel.

Example: iterate a 60/25/15 peak mix without script sprawl

  1. Import Postman collections for mobile and web checkout paths (browse GET, cart POST, checkout POST). Problem solved: one source of truth when merchandising updates endpoints.
  2. Create three parallel scenarios in the editor—browse 60 req/s, cart 25 req/s, checkout 15 req/s—with tags journey:browse, journey:cart, journey:checkout. Problem solved: honest funnel mix without hand-editing four script files.
  3. Add a spike overlay on checkout: ramp to 40 req/s for five minutes mid-run. Problem solved: doorbuster rehearsal is a parameter change, not a new repo.
  4. Set checkout thresholds (p95 < 1500 ms, failures < 2%) in the UI and run the 20-minute dress rehearsal. Problem solved: leadership reads the same report engineering uses.
  5. Compare runs week-over-week in the integrated view as coupon rules and catalog size change. Problem solved: regressions visible before code freeze.
  6. Export k6 script for nightly CI smoke on checkout-only paths after integrations merge.

That workflow matches this post's cta: run, tune, and report peak scenarios from one desktop app while the calendar compresses toward Black Friday.

Closing takeaway

Black Friday readiness is a checkout and payments problem disguised as a traffic graph. Model browse-to-buy mixes, spike checkout independently, and threshold the journeys that carry revenue—not vanity homepage hits.

Run this week's funnel mix against staging before merchandising locks—and note whether checkout tail latency survives the five-minute spike your promo email will trigger.

Try Performate free | Holiday traffic patterns | k6 thresholds

Ready to optimize your API performance?

Download Performate to run, tune, and report on these k6 workflows from one desktop app built for busy teams.

← Back to all posts