Thread Groups & Realistic Load
A common mistake in performance testing: configuring 1,000 users to hit your server simultaneously and wondering why everything crashes. Real users don't behave that way.
This lesson teaches you to create realistic load patterns that reveal true performance issues.
Thread Group Fundamentals
The Three Core Settings
| Setting | What It Controls | Typical Value |
|---|---|---|
| Number of Threads | Total virtual users | 10-10,000 |
| Ramp-up Period | Time to start all threads | 30-300 seconds |
| Loop Count | Iterations per thread | 1-100 or Forever |
Understanding Ramp-up
With 100 threads and 60-second ramp-up:
Time 0s: ~1-2 threads active
Time 15s: ~25 threads active
Time 30s: ~50 threads active
Time 45s: ~75 threads active
Time 60s: 100 threads active
Calculation: New thread every ramp-up / threads seconds = 60/100 = 0.6 seconds per new thread.
Why Ramp-up Matters
Bad: 0s Ramp-up
All users hit at once. Server sees instant spike. May trigger rate limiting or false failures.
Good: Gradual Ramp-up
Users arrive gradually. Server warms up. Caches populate. Realistic traffic pattern.
Thread Group Configuration
Basic Thread Group XML
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="Product Shoppers">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">60</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
<stringProp name="ThreadGroup.delay">0</stringProp>
</ThreadGroup>
Scheduler for Timed Tests
Enable the scheduler to run tests for a specific duration:
☑ Scheduler
Duration (seconds): 300 (5 minutes)
Startup delay (seconds): 0
This runs the test for exactly 5 minutes, regardless of loop count.
Think Time: Simulating Real Users
Real users don't make requests continuously. They:
- Read product descriptions (5-15 seconds)
- Compare prices (10-30 seconds)
- Fill out checkout forms (30-60 seconds)
- Think about purchases (varies widely)
Constant Timer
Adds a fixed delay after each request:
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer"
testname="Read Product Page">
<stringProp name="ConstantTimer.delay">5000</stringProp>
</ConstantTimer>
Problem: All users pause for exactly 5 seconds. Unrealistic.
Gaussian Random Timer (Recommended)
Adds variable delay with normal distribution:
<GaussianRandomTimer guiclass="GaussianRandomTimerGui"
testclass="GaussianRandomTimer"
testname="Realistic Think Time">
<stringProp name="ConstantTimer.delay">5000</stringProp>
<stringProp name="RandomTimer.range">2000</stringProp>
</GaussianRandomTimer>
This creates think times between 3-7 seconds (5000ms +/- 2000ms), following a bell curve.
Uniform Random Timer
Random delay within a range (flat distribution):
<UniformRandomTimer guiclass="UniformRandomTimerGui"
testclass="UniformRandomTimer"
testname="Variable Delay">
<stringProp name="ConstantTimer.delay">3000</stringProp>
<stringProp name="RandomTimer.range">4000</stringProp>
</UniformRandomTimer>
Delay ranges from 3000ms to 7000ms, with equal probability for any value.
E-commerce Think Time Examples
| Action | Realistic Think Time |
|---|---|
| Browse product listing | 2-5 seconds |
| View product details | 5-15 seconds |
| Add to cart | 1-2 seconds |
| View cart | 3-5 seconds |
| Fill checkout form | 30-60 seconds |
| Payment confirmation | 5-10 seconds |
<!-- After viewing product details -->
<GaussianRandomTimer guiclass="GaussianRandomTimerGui"
testclass="GaussianRandomTimer"
testname="Read Product Details">
<stringProp name="ConstantTimer.delay">10000</stringProp>
<stringProp name="RandomTimer.range">5000</stringProp>
</GaussianRandomTimer>
<!-- After filling checkout form -->
<GaussianRandomTimer guiclass="GaussianRandomTimerGui"
testclass="GaussianRandomTimer"
testname="Fill Checkout Form">
<stringProp name="ConstantTimer.delay">45000</stringProp>
<stringProp name="RandomTimer.range">15000</stringProp>
</GaussianRandomTimer>
Load Patterns for E-commerce
Pattern 1: Steady State
Constant load to establish baseline performance:
Users: 500 (constant)
Duration: 30 minutes
Use case: Normal business hours
<ThreadGroup testname="Steady State">
<stringProp name="ThreadGroup.num_threads">500</stringProp>
<stringProp name="ThreadGroup.ramp_time">120</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
Pattern 2: Step-up Load
Gradually increase to find breaking point:
Start: 100 users
Add: 100 users every 5 minutes
Max: 1000 users
Use the Stepping Thread Group plugin or multiple Thread Groups with delays:
<!-- Batch 1: Start immediately -->
<ThreadGroup testname="Users Batch 1">
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">30</stringProp>
<stringProp name="ThreadGroup.delay">0</stringProp>
</ThreadGroup>
<!-- Batch 2: Start after 5 minutes -->
<ThreadGroup testname="Users Batch 2">
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">30</stringProp>
<stringProp name="ThreadGroup.delay">300</stringProp>
</ThreadGroup>
<!-- Continue pattern... -->
Pattern 3: Spike Test (Flash Sale)
Sudden traffic surge:
Baseline: 200 users
Spike: 2000 users in 30 seconds
Duration of spike: 10 minutes
Return to baseline
<!-- Background traffic -->
<ThreadGroup testname="Regular Shoppers">
<stringProp name="ThreadGroup.num_threads">200</stringProp>
<stringProp name="ThreadGroup.ramp_time">60</stringProp>
<stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
<!-- Flash sale spike -->
<ThreadGroup testname="Flash Sale Rush">
<stringProp name="ThreadGroup.num_threads">1800</stringProp>
<stringProp name="ThreadGroup.ramp_time">30</stringProp>
<stringProp name="ThreadGroup.delay">300</stringProp>
<stringProp name="ThreadGroup.duration">600</stringProp>
</ThreadGroup>
Multiple Thread Groups
You can have multiple Thread Groups to simulate different user types:
Test Plan
├── Thread Group: Browsers (70% - just looking)
│ └── Browse products, no purchase
├── Thread Group: Buyers (25% - complete purchase)
│ └── Full checkout flow
└── Thread Group: Admins (5% - inventory updates)
└── Backend operations
<ThreadGroup testname="Browsers">
<stringProp name="ThreadGroup.num_threads">700</stringProp>
<!-- Just browse products -->
</ThreadGroup>
<ThreadGroup testname="Buyers">
<stringProp name="ThreadGroup.num_threads">250</stringProp>
<!-- Complete checkout flow -->
</ThreadGroup>
<ThreadGroup testname="Admins">
<stringProp name="ThreadGroup.num_threads">50</stringProp>
<!-- Backend operations -->
</ThreadGroup>
Calculating Thread Count
Based on Expected Traffic
Peak users: 10,000 users/hour
Average session: 10 minutes
Concurrent users = (10,000 / 60) * 10 = ~1,667 concurrent
Add safety margin (50%): ~2,500 threads for testing
Based on Target Throughput
Target: 100 requests/second
Average think time: 5 seconds
Average requests per iteration: 10
Threads needed = (Target RPS * Think Time) / Requests per iteration
Threads = (100 * 5) / 10 = 50 threads
Add for variation: 75-100 threads
Common Mistakes
Without think time, 100 threads can generate 10,000+ requests/second - far more than 100 real users would generate. Always add realistic delays.
Ramping up 1,000 users in 10 seconds creates artificial spike. Use at least 1 second per 10 users as a minimum.
If you want a 30-minute test, use the scheduler with duration, not a fixed loop count. Loop count varies based on response times.
Key Takeaways
- Ramp-up gradually: At least 1 second per 10 threads to avoid unrealistic spikes.
- Always add think time: Real users pause between actions. Use Gaussian Random Timer for realism.
- Match real patterns: Use scheduler for duration-based tests, multiple thread groups for user types.
- Calculate threads from traffic: Base thread count on expected concurrent users, not total daily visitors.
Next Lesson
Your threads are configured realistically. But here's the catch: modern web apps use sessions, tokens, and dynamic data. Next, we'll master Correlation - the high-value skill that makes tests actually work with real applications.