The constant-arrival-rate executor starts iterations at a fixed rate (iterations per second), independent of how long each iteration takes. This executor is ideal when you need to maintain a specific throughput or requests-per-second rate.
How It Works
With constant arrival rate:
- Iterations start at a fixed rate (e.g., 10 iterations/second)
- k6 automatically allocates VUs as needed to maintain the rate
- If iterations are fast, fewer VUs are needed
- If iterations are slow, more VUs are allocated (up to
maxVUs)
- If
maxVUs is reached and rate can’t be maintained, iterations are dropped
Iteration Rate: 10 iters/sec
Fast iterations (100ms):
VU 1: [i][i][i][i][i][i][i][i][i][i] (handles all 10/sec)
Slow iterations (500ms):
VU 1: [----i----][----i----] (2/sec)
VU 2: [----i----][----i----] (2/sec)
VU 3: [----i----][----i----] (2/sec)
VU 4: [----i----][----i----] (2/sec)
VU 5: [----i----][----i----] (2/sec)
Total: 10 iters/sec with 5 VUs
Configuration
Must be constant-arrival-rate
Number of iterations to start per timeUnit period. Must be greater than 0.
Period over which the rate is calculated. Must be greater than 0.
Total duration of the executor. Must be at least 1 second.
Number of VUs to pre-allocate before test start. These VUs are immediately available.
maxVUs
integer
default:"preAllocatedVUs"
Maximum number of VUs allowed. If iterations require more VUs, they will be dropped. Cannot be less than preAllocatedVUs.
Example
Basic Throughput Test
import http from 'k6/http';
export const options = {
scenarios: {
constant_rps: {
executor: 'constant-arrival-rate',
rate: 30, // 30 iterations per second
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50, // Start with 50 VUs
maxVUs: 100, // Allow up to 100 VUs
},
},
};
export default function () {
http.get('https://test.k6.io');
}
API Rate Limit Testing
import http from 'k6/http';
import { check } from 'k6';
export const options = {
scenarios: {
api_rate_limit: {
executor: 'constant-arrival-rate',
rate: 100, // 100 requests per second
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 10,
maxVUs: 200,
},
},
thresholds: {
http_req_failed: ['rate<0.05'], // Less than 5% errors
dropped_iterations: ['rate<0.01'], // Less than 1% dropped
},
};
export default function () {
const res = http.get('https://test.k6.io/api');
check(res, {
'status is 200': (r) => r.status === 200,
'not rate limited': (r) => r.status !== 429,
});
}
Different Time Units
export const options = {
scenarios: {
// 100 iterations per minute (1.67/sec)
per_minute: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1m',
duration: '10m',
preAllocatedVUs: 5,
maxVUs: 10,
},
},
};
When to Use
Use the constant arrival rate executor when:
- You need to maintain a specific requests-per-second (RPS) rate
- Iteration duration varies significantly or is unpredictable
- You want to test system throughput capacity
- You’re testing against rate limits
- You need consistent load regardless of response times
- You want to model realistic arrival patterns (Poisson distribution)
- Response time degradation shouldn’t reduce load
Behavior Details
Dynamic VU Allocation
k6 automatically adjusts the number of active VUs to maintain the iteration rate:
// If response time increases from 100ms to 500ms:
// VUs automatically increase from ~2 to ~10 to maintain rate
Pre-allocated vs Max VUs
- preAllocatedVUs: VUs initialized at test start (instant availability)
- maxVUs: Maximum VUs that can be used (allocated on-demand)
{
preAllocatedVUs: 20, // 20 VUs ready immediately
maxVUs: 100, // Can grow to 100 if needed
}
// If rate needs 30 VUs, 10 more are allocated dynamically
// If rate needs 150 VUs, only 100 are used, rest is dropped
Dropped Iterations
When maxVUs is reached and can’t maintain the rate, iterations are dropped:
export const options = {
scenarios: {
insufficient_vus: {
executor: 'constant-arrival-rate',
rate: 100,
duration: '5m',
preAllocatedVUs: 10,
maxVUs: 10, // Too few VUs!
},
},
thresholds: {
dropped_iterations: ['count<100'], // Alert if too many dropped
},
};
Monitor the dropped_iterations metric to detect insufficient VU allocation.
Calculation: Rate to Iterations/Second
// rate: 30, timeUnit: '1s' = 30 iters/sec
// rate: 60, timeUnit: '1m' = 1 iter/sec
// rate: 100, timeUnit: '10s' = 10 iters/sec
itersPerSecond = rate / timeUnit_in_seconds
Common Patterns
Capacity Testing
Find maximum sustainable throughput:
export const options = {
scenarios: {
capacity_test: {
executor: 'constant-arrival-rate',
rate: 500, // Target: 500 RPS
duration: '10m',
preAllocatedVUs: 100,
maxVUs: 500, // Allow plenty of headroom
},
},
thresholds: {
http_req_duration: ['p(95)<1000'], // 95th percentile under 1s
http_req_failed: ['rate<0.01'], // Less than 1% errors
dropped_iterations: ['rate==0'], // No dropped iterations
},
};
Realistic Traffic Simulation
import http from 'k6/http';
import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
const endpoints = [
'/api/users',
'/api/products',
'/api/orders',
];
export const options = {
scenarios: {
api_traffic: {
executor: 'constant-arrival-rate',
rate: 50, // 50 RPS average production rate
duration: '30m',
preAllocatedVUs: 20,
maxVUs: 100,
},
},
};
export default function () {
http.get(`https://test.k6.io${randomItem(endpoints)}`);
}
Combined with Ramping
Use multiple scenarios to ramp arrival rate:
export const options = {
scenarios: {
warmup: {
executor: 'constant-arrival-rate',
rate: 10,
duration: '2m',
preAllocatedVUs: 5,
maxVUs: 20,
},
normal: {
executor: 'constant-arrival-rate',
rate: 50,
duration: '10m',
startTime: '2m',
preAllocatedVUs: 20,
maxVUs: 100,
},
peak: {
executor: 'constant-arrival-rate',
rate: 200,
duration: '5m',
startTime: '12m',
preAllocatedVUs: 50,
maxVUs: 300,
},
},
};
Sizing VUs
Calculate required VUs:
Required VUs ≈ (rate × avg_iteration_duration) / timeUnit
Example:
- Rate: 100 iterations/second
- Average iteration: 500ms
- Required VUs ≈ 100 × 0.5 = 50 VUs
Recommended:
- preAllocatedVUs: 50 (expected)
- maxVUs: 100 (2x safety margin)
Metrics
Key metrics for arrival rate testing:
iterations - Total completed iterations
iteration_duration - How long each iteration takes
dropped_iterations - Iterations dropped due to insufficient VUs
vus - Current number of active VUs
vus_max - Current number of allocated VUs (up to maxVUs)
Best Practices
- Set maxVUs generously: Allow 2-3x the expected VUs to handle variability
- Monitor dropped_iterations: Set thresholds to alert on dropped iterations
- Right-size preAllocatedVUs: Match expected steady-state requirements
- Consider iteration duration: Longer iterations need more VUs for same rate
- Test VU allocation: Run short tests to verify VU requirements
- Use for throughput testing: Better than constant-vus for testing capacity
- Watch for rate limiting: Monitor for 429 responses when testing limits
Comparison with Constant VUs
| Feature | Constant Arrival Rate | Constant VUs |
|---|
| Load model | Fixed iterations/sec | Fixed VUs |
| VU count | Dynamic (auto-scaled) | Static |
| Throughput | Constant (if possible) | Variable |
| When response slows | More VUs allocated | Throughput decreases |
| Complexity | Higher | Lower |
| Best for | Throughput/capacity tests | User simulation |
Troubleshooting
Too Many Dropped Iterations
// Problem: dropped_iterations is high
// Solution: Increase maxVUs
{
maxVUs: 200, // Increase from 100
}
VUs Not Fully Utilized
// Problem: vus much less than preAllocatedVUs
// Solution: Reduce preAllocatedVUs to save resources
{
preAllocatedVUs: 10, // Reduce from 50
}
See Also