k6 HTTP Batch Request Timing Issues Fix
In this tutorial, you'll learn about k6 http batch request timing issues fix. We cover key concepts, practical examples, and best practices.
Your k6 http.batch() returns mixed results — some requests succeed while others fail, and you cannot distinguish their individual timing. Batch requests run in parallel but report aggregated metrics, hiding per-endpoint performance.
The Problem
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const responses = http.batch([
['GET', 'https://api.example.com/users'],
['GET', 'https://api.example.com/orders'],
['GET', 'https://api.example.com/products'],
]);
// Cannot tell which endpoint failed
responses.forEach((res) => {
check(res, { 'status is 200': (r) => r.status === 200 });
});
}
✓ status is 200
http_req_duration........: avg=234ms p(95)=890ms
One endpoint might be slow (890ms p95) but you cannot tell which one.
Step-by-Step Fix
1. Name batch requests individually
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const responses = http.batch([
{ method: 'GET', url: 'https://api.example.com/users', tags: { name: 'UsersAPI' } },
{ method: 'GET', url: 'https://api.example.com/orders', tags: { name: 'OrdersAPI' } },
{ method: 'GET', url: 'https://api.example.com/products', tags: { name: 'ProductsAPI' } },
]);
responses.forEach((res, i) => {
const endpoints = ['Users', 'Orders', 'Products'];
console.log(`${endpoints[i]} API: ${res.status} (${res.timings.duration}ms)`);
});
}
2. Add per-endpoint thresholds
export const options = {
thresholds: {
'http_req_duration{name:UsersAPI}': ['p(95)<500'],
'http_req_duration{name:OrdersAPI}': ['p(95)<2000'],
'http_req_duration{name:ProductsAPI}': ['p(95)<1000'],
},
};
3. Handle partial batch failures
import http from 'k6/http';
import { check, fail } from 'k6';
export default function () {
const requests = [
{ method: 'GET', url: 'https://api.example.com/users', tags: { name: 'UsersAPI' } },
{ method: 'GET', url: 'https://api.example.com/orders', tags: { name: 'OrdersAPI' } },
];
const responses = http.batch(requests);
const failed = responses.filter((r) => r.status !== 200);
if (failed.length > 0) {
console.log(`${failed.length} requests failed in batch`);
failed.forEach((r) => console.log(` ${r.url}: ${r.status}`));
}
}
4. Measure batch response time manually
import http from 'k6/http';
import { Trend } from 'k6/metrics';
const batchDuration = new Trend('batch_duration');
export default function () {
const start = Date.now();
const responses = http.batch([
{ method: 'GET', url: 'https://api.example.com/users', tags: { name: 'UsersAPI' } },
{ method: 'GET', url: 'https://api.example.com/orders', tags: { name: 'OrdersAPI' } },
]);
batchDuration.add(Date.now() - start);
const allOk = responses.every((r) => r.status === 200);
console.log(`Batch completed in ${Date.now() - start}ms — ${allOk ? 'all OK' : 'some failed'}`);
}
5. Use separate http.get calls for critical endpoints
import http from 'k6/http';
import { check } from 'k6';
export default function () {
// Critical endpoint measured individually
const users = http.get('https://api.example.com/users', { tags: { name: 'UsersCritical' } });
check(users, { 'users status is 200': (r) => r.status === 200 });
// Non-critical endpoints in batch
http.batch([
['GET', 'https://api.example.com/analytics'],
['GET', 'https://api.example.com/health'],
]);
}
Expected output:
http_req_duration{name:UsersAPI}.......: avg=45ms p(95)=120ms
http_req_duration{name:OrdersAPI}......: avg=320ms p(95)=890ms
http_req_duration{name:ProductsAPI}....: avg=67ms p(95)=150ms
Prevention Tips
- Tag each batch request with a unique
namefor per-endpoint metrics - Set per-endpoint thresholds for granular pass/fail criteria
- Handle partial failures explicitly in your script
- Measure total batch duration with a custom Trend metric
- Use individual
http.get()for critical endpoints that need separate timing
Common Mistakes with http batch
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations
These mistakes appear frequently in real-world K6 code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro