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
| Situation | Recommended action |
|---|---|
| First Black Friday on new stack | Start from forecast; validate browse/checkout split weekly |
| Repeat storefront with analytics | Pull last year's funnel rates; add 20% headroom on checkout |
| Heavy promo / doorbuster SKUs | Dedicated spike scenario on checkout + inventory SKUs |
| PSP latency unknown | Measure sandbox p95; add env sleep to match (idempotency flows) |
| Staging smaller than prod | Scale 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:checkouttags 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
- 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.
- 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. - 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.
- 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. - Compare runs week-over-week in the integrated view as coupon rules and catalog size change. Problem solved: regressions visible before code freeze.
- 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.