Skip to main content
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:
  1. Iterations start at a fixed rate (e.g., 10 iterations/second)
  2. k6 automatically allocates VUs as needed to maintain the rate
  3. If iterations are fast, fewer VUs are needed
  4. If iterations are slow, more VUs are allocated (up to maxVUs)
  5. 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

executor
string
required
Must be constant-arrival-rate
rate
integer
required
Number of iterations to start per timeUnit period. Must be greater than 0.
timeUnit
duration
default:"1s"
Period over which the rate is calculated. Must be greater than 0.
duration
duration
required
Total duration of the executor. Must be at least 1 second.
preAllocatedVUs
integer
required
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

  1. Set maxVUs generously: Allow 2-3x the expected VUs to handle variability
  2. Monitor dropped_iterations: Set thresholds to alert on dropped iterations
  3. Right-size preAllocatedVUs: Match expected steady-state requirements
  4. Consider iteration duration: Longer iterations need more VUs for same rate
  5. Test VU allocation: Run short tests to verify VU requirements
  6. Use for throughput testing: Better than constant-vus for testing capacity
  7. Watch for rate limiting: Monitor for 429 responses when testing limits

Comparison with Constant VUs

FeatureConstant Arrival RateConstant VUs
Load modelFixed iterations/secFixed VUs
VU countDynamic (auto-scaled)Static
ThroughputConstant (if possible)Variable
When response slowsMore VUs allocatedThroughput decreases
ComplexityHigherLower
Best forThroughput/capacity testsUser 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

Build docs developers (and LLMs) love