Request Lifecycle: How Web Frameworks Handle a Request · Capstone Case Studies
Capstone: Trace a Slow Request Through Every Layer
A Laravel checkout behind a load balancer, nginx and PHP-FPM has a p99 of 6 seconds. Find where the time goes, layer by layer, and fix it.
Scenario
Example company: an online shop runs Laravel 12 on PHP-FPM behind nginx and a cloud load balancer. The median checkout request takes 280 ms, but the 99th percentile is 6 seconds, and a few requests a day end in 504.
What the team knows:
pm.max_children = 20,request_terminate_timeoutis unset, nginxproxy_read_timeoutis 60 s, the load balancer idle timeout is 60 s.- Checkout calls a fraud-check API (no timeout set), the payment provider (10 s timeout), and writes to MySQL in a transaction.
- A
SettingsServiceProviderand aGeoIpMiddleware(global) were added last quarter. - Logs have no request IDs; nginx, PHP-FPM and Laravel logs can't be correlated.
Requirements
- Explain where the p99 time goes, with the evidence you would collect at each layer.
- Bring checkout p99 under 1.5 s and eliminate 504s.
- Make any future slow request traceable end to end within minutes.
Constraints
- The payment provider sometimes needs up to 8 s; you can't change that.
- No new infrastructure beyond what Laravel and the cloud provider offer.
Deliverables
- A layer-by-layer map of the request (load balancer → nginx → PHP-FPM → Laravel bootstrap → middleware → controller → outbound calls → response → terminate), with the measurement you'd take at each.
- A ranked list of suspected causes with the evidence that would confirm each.
- The fixes: timeouts at every layer (inner shorter than outer), changes to providers and middleware, what moves to queues, and FPM pool settings.
- The tracing plan: request IDs or W3C trace context from the load balancer to the database, and the dashboards and alerts you'd add.
Hints
- Queueing in a saturated FPM pool shows up as time before Laravel starts; compare nginx upstream connect/header times with Laravel's own timing.
- Global middleware and eager providers add cost to every request, including checkout.
- Payment can stay synchronous with a strict budget; the fraud check needs a timeout and a fallback; emails and analytics belong in queues or terminate callbacks.
Deliverables
Which measurements would you take at each layer?
Model answer
Load balancer and nginx request/upstream timings, FPM status (active/idle workers, listen queue, max children reached), Laravel timing from start to first middleware (bootstrap) and per-middleware/controller spans, outbound call durations, and DB query times, all correlated by a request ID.
Rubric
Look for evidence at every layer and correlation by request ID, rather than guessing from application logs alone.
What would you change to remove the 504s?
Model answer
Set inner timeouts shorter than outer ones (outbound calls < PHP request timeout < nginx < load balancer), add a fraud-check timeout with fallback, size the FPM pool by memory, and move non-essential work to queues or terminate callbacks.
Rubric
Strong answers align timeouts, protect worker capacity, and keep the critical path short.
How do you make future slow requests traceable?
Model answer
Generate or propagate a request ID / W3C traceparent at the load balancer, log it in nginx and Laravel, pass it to outbound calls and queued jobs, and use APM tracing with spans for middleware, controller, queries and HTTP calls.
Rubric
Look for end-to-end propagation, including queues and outbound calls.
Open in the app to mark lessons complete, take notes and track your progress.