Op Black Friday verwerkte één van onze klanten 47.000 orders in 6 uur. De server stond nooit stil. Hier is de architectuur die dat mogelijk maakte.
Laravel op hoge belasting — Black Friday overleven
Op Black Friday verwerkte één van onze klanten 47.000 orders in 6 uur. De server stond nooit stil. Response times bleven onder de 200ms. Geen downtime, geen verloren orders.
Het jaar ervoor crashte hun platform bij 3.000 orders in twee uur.
Het verschil zat niet in betere hardware. Het zat in architectuur.
Wat je leert in dit artikel
- Hoe je een Laravel applicatie voorbereidt op extreme piekbelasting
- Load testing: hoe je weet of je applicatie het houdt
- Horizontal scaling met queue workers
- Database-optimalisaties die het meeste effect hebben
- Caching strategieën die Black Friday-verkeer opvangen
De bottlenecks begrijpen voordat je optimaliseert
Optimaliseren zonder meten is gissen. Voordat je iets verandert, weet je moeten weten waar de bottleneck zit.
Er zijn vier typische bottlenecks bij hoge belasting.
Database queries. De meest voorkomende oorzaak van trage applicaties. Een query die 50ms kost bij normaal verkeer, kost 5 seconden als honderd gebruikers tegelijk hetzelfde opvragen. Synchrone verwerking. Alles wat tijdens een request gebeurt, vertraagt de response. Order confirmation emails, ERP-synchronisatie, PDF-generatie — dit hoort niet in de request cycle thuis. Ontbrekende caching. Productpagina's, categoriepagina's, prijslijsten — data die nauwelijks verandert maar bij elke request opnieuw uit de database wordt gehaald. Te weinig workers. Queue workers verwerken achtergrondtaken. Als er te weinig zijn, groeit de queue en worden taken pas uren later verwerkt.Stap 1: load testing voordat het Black Friday is
Je wilt weten waar je applicatie breekt vóórdat je klanten dat ontdekken.
Gebruik k6 voor load testing. Het is open source, scriptbaar in JavaScript en geeft gedetailleerde rapportages.
// k6 load test: simuleert 500 gelijktijdige gebruikers
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
stages: [
// Langzaam opbouwen naar piekbelasting
{ duration: '2m', target: 100 },
{ duration: '5m', target: 500 },
// Piek vasthouden
{ duration: '10m', target: 500 },
// Afbouwen
{ duration: '2m', target: 0 },
],
thresholds: {
// 95% van requests moet onder 500ms blijven
http_req_duration: ['p(95)<500'],
// Maximaal 1% errors
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const response = http.get('https://jouwshop.nl/checkout');
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
Draai deze test op een staging-omgeving die identiek is aan productie. Kijk waar response times omhoog gaan. Dat is je bottleneck.
Stap 2: alles uit de request cycle halen wat er niet in hoort
Een checkout request doet in de meeste applicaties te veel. Order opslaan, betaling initiëren, bevestigingsmail sturen, ERP notificeren, PDF aanmaken, voorraad bijwerken.
Dat alles synchronous uitvoeren betekent dat jouw klant wacht op alles tegelijk.
De vuistregel: alles wat niet direct nodig is voor de response van de klant, hoort in een queue.
// Fout: alles synchronous in de controller
public function store(CheckoutRequest $request): JsonResponse
{
$order = Order::create($request->validated());
// Klant wacht op al het volgende:
Mail::to($order->customer)->send(new OrderConfirmation($order));
$this->erpService->syncOrder($order);
$this->pdfService->generateInvoice($order);
$this->inventoryService->decrementStock($order);
return response()->json(['order_id' => $order->id]);
}
// Goed: alleen het minimale synchronous, rest in queue
public function store(CheckoutRequest $request): JsonResponse
{
$order = Order::create($request->validated());
// Klant krijgt direct een response
// De rest gebeurt op de achtergrond
SendOrderConfirmation::dispatch($order);
SyncOrderToErp::dispatch($order);
GenerateInvoicePdf::dispatch($order);
DecrementInventory::dispatch($order);
return response()->json(['order_id' => $order->id]);
}
Op Black Friday verwerkte onze klant bevestigingsmails via de queue. De klant zag direct een bevestigingspagina. De mail volgde binnen dertig seconden. Niemand klaagde.
Stap 3: database-optimalisatie
Database queries zijn de meest voorkomende oorzaak van trage Laravel applicaties.
Gebruik Laravel Telescope of Debugbar om N+1 queries te vinden.// N+1 probleem: 1 query voor orders + 1 per order voor klant
$orders = Order::all();
foreach ($orders as $order) {
echo $order->customer->name; // Extra query per order
}
// Oplossing: eager loading
$orders = Order::with('customer')->get();
foreach ($orders as $order) {
echo $order->customer->name; // Geen extra queries
}
// Migration: index toevoegen op veelgebruikte filterkolommen
Schema::table('orders', function (Blueprint $table) {
$table->index(['status', 'created_at']);
$table->index('customer_id');
});
// Fout: alle orders in geheugen laden
$orders = Order::where('status', 'pending')->get();
// Goed: in chunks verwerken
Order::where('status', 'pending')->chunk(500, function ($orders) {
foreach ($orders as $order) {
// Verwerk order
}
});
Stap 4: caching strategisch inzetten
Op Black Friday is caching het verschil tussen overleven en crashen.
Cache productdata en prijzen. Deze veranderen zelden. Elke milliseconde die je bespaart op productpagina's telt op bij hoge belasting.// Cache productdata voor 15 minuten
$product = Cache::remember("product.{$productId}", 900, function () use ($productId) {
return Product::with(['images', 'attributes', 'categories'])
->findOrFail($productId);
});
SESSION_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
// Cache de Black Friday categorie voor 5 minuten
$featuredProducts = Cache::tags(['products', 'black-friday'])
->remember('black-friday-products', 300, function () {
return Product::onSale()->limit(24)->get();
});
Stap 5: horizontal scaling met queue workers
Meer queue workers = meer taken parallel verwerken. Op Black Friday schaalde onze klant van 4 naar 32 workers.
; Supervisor configuratie voor queue workers
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app/artisan queue:work redis --sleep=3 --tries=3
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
; Schaal dit op naar 32 tijdens Black Friday
numprocs=8
Op cloud-omgevingen (DigitalOcean, AWS) kun je workers automatisch laten schalen op basis van queue-lengte.
De resultaten
| Metric | Voor optimalisatie | Na optimalisatie |
|---|---|---|
| Max concurrent requests | ~200 | 2.000+ |
| Gemiddelde response time | 890ms | 145ms |
| Orders per uur | ~1.500 | 7.800 |
| Queue verwerkingstijd | Real-time (sync) | <30 seconden |
| Downtime Black Friday | 2+ uur | 0 minuten |
Samenvatting
Black Friday is geen dag om te testen of je architectuur werkt. Het is de dag die aantoont of je het goed hebt gedaan.
De vijf stappen: load testen, synchrone taken naar queues, N+1 queries oplossen, caching op de juiste plekken, en workers schalen. Dat is het. Geen magie.
Meer weten over Laravel-architectuur? Lees ook ons artikel over e-commerce offloading en checkout performance. Of bekijk wat wij bouwen op de Laravel pagina. De officiële Laravel documentatie over queues en Laravel Horizon zijn essentieel leesvoer voor deze architectuur.
Staat jouw Black Friday op de planning en wil je weten of jouw platform het aankan? Neem contact op.

Geschreven door Ruthger Idema
15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.
Meer over ons team →