The load-testing lesson before this one introduced the tools (JMeter, Locust) — this one is about the discipline of actually using them well. Pointing a traffic generator at your homepage and cranking up the concurrency number tells you almost nothing useful on its own. Real traffic isn't uniform: it hits different pages, some visitors are logged in and some aren't, and it arrives in bursts rather than a perfectly smooth stream. Simulating that shape, not just a raw number, is what separates a load test that predicts real-world behavior from one that just makes a number go up.
What you'll learn in this lesson
- Why testing a single URL in a tight loop gives you a misleading picture of real capacity
- How to model a realistic traffic mix across anonymous and authenticated requests
- The difference between ramping load gradually versus a sudden spike, and why both matter
- What to actually watch on the server side while a test runs
Modeling a realistic traffic mix
A production Drupal site's traffic is a mix: mostly anonymous visitors hitting cached pages (fast, cheap), a smaller slice of logged-in users hitting uncached dynamic pages (slow, expensive), and an even smaller slice submitting forms (slowest, writes to the database). A test that only hits the cached homepage will report numbers that are wildly optimistic compared to what happens when real users start logging in and searching.
# Example traffic-mix weighting for a load-testing tool's scenario config
# (syntax varies by tool; the ratios are the part that matters).
scenarios:
- name: anonymous_browse
weight: 70
path: /
- name: anonymous_search
weight: 15
path: /search?keys=drupal
- name: authenticated_dashboard
weight: 10
path: /user
requires_login: true
- name: form_submission
weight: 5
path: /node/add/article
requires_login: true
method: POST
The weights should come from your actual site's analytics, not a guess — check your web server logs or analytics tool for the real ratio of anonymous-to-authenticated traffic before picking these numbers.
Ramp-up versus spike
A gradual ramp (say, adding 10 concurrent users every 30 seconds up to a target) tells you your sustained capacity — the traffic level you can serve indefinitely. A sudden spike (jumping straight to peak concurrency) tells you something different: how your site behaves under a shock, like a link going viral or a marketing email blast landing all at once. Both matter, and they often reveal different bottlenecks — a gradual ramp usually finds database connection-pool limits first, while a sudden spike is more likely to expose PHP-FPM's max-children setting being too low, causing requests to queue and time out instead of failing gracefully.
What to watch while the test runs
The load-testing tool's own report (response times, error rate) is only half the picture. Watch the server itself in a second terminal while the test runs: top or htop for CPU and memory pressure, your database's own slow-query log, and PHP-FPM's status page (pm.status_path in its pool config) to see whether you're running out of available worker processes before you run out of raw CPU.
See it for yourself
Run a short test against your own DDEV site with a realistic mix like the one above (start small — 5-10 concurrent users for a minute) and watch ddev logs -s web -f in a second terminal alongside it. You should see response times creep up as concurrency increases, and you're looking for the exact point where that curve stops being gradual and starts climbing steeply — that inflection point is your practical capacity ceiling.
Quick check: your test shows response times staying flat and low right up until 200 concurrent users, then suddenly spiking to 10+ seconds. What does that shape suggest, versus a test where response times climb smoothly the whole way up? (A sudden cliff usually means you hit a hard resource limit — PHP-FPM's max_children, a database connection pool cap, or available memory running out — not gradual degradation. A smooth climb usually means the bottleneck scales with load more continuously, like CPU contention. The shape of the curve, not just the numbers, tells you what kind of limit you're up against.)
Key takeaways
- Weight your test scenarios to match real traffic (mostly anonymous/cached, less authenticated, least form submission) — a single-URL loop test is misleading.
- Test both a gradual ramp (sustained capacity) and a sudden spike (shock behavior) — they surface different bottlenecks.
- Discard cold-cache warm-up results when measuring steady-state performance.
- Watch server-side metrics (CPU, PHP-FPM worker availability, database slow-query log) alongside the tool's own report — the tool only tells you the symptom, not the cause.
Coming up next
You've now generated the data — response times, error rates, and a resource-usage picture from a realistic simulated load. The final lesson in this section covers how to actually analyze that data to pinpoint the specific bottleneck, rather than just knowing that one exists.