Coordinated Omission và Sai lầm Đo lường Latency
Các sai lầm phổ biến nhất khi đo latency trong hệ thống phân tán: bài toán Coordinated Omission, trung bình hóa percentile, và phương pháp load testing chính xác.
Tổng quan
Trong bài talk nổi tiếng "How NOT to Measure Latency", Gil Tene đã chỉ ra rằng phần lớn các benchmark và load test đều cho kết quả lạc quan giả tạo (falsely optimistic) do các lỗi đo lường có hệ thống (systematic measurement errors).
Vấn đề không nằm ở tool hay infrastructure — mà nằm ở phương pháp đo. Khi bạn đo sai, mọi quyết định về capacity planning, SLA, và performance tuning đều dựa trên dữ liệu không đáng tin cậy. Hệ thống tưởng chạy tốt ở production, nhưng thực tế đang âm thầm vi phạm SLO mà bạn không hay biết.
Bài viết này tổng hợp ba sai lầm phổ biến nhất: Coordinated Omission, trung bình hóa percentile, và thiếu mẫu thống kê — cùng các phương pháp khắc phục thực tế.
Xem thêm nền tảng về Latency Percentile Metrics và Latency & Throughput Overview trước khi đi sâu vào các sai lầm đo lường.
Coordinated Omission — Bẫy đo lường nguy hiểm nhất
Coordinated Omission là hiện tượng xảy ra khi load test tool vô tình "phối hợp" với server để bỏ sót (omit) các request lẽ ra phải được gửi đi trong khoảng thời gian server bị chậm.
Cơ chế xảy ra
Hầu hết các tool load test (ab, wrk, JMeter mặc định) hoạt động theo mô hình closed-loop: chỉ gửi request tiếp theo sau khi request trước đã nhận response. Khi server gặp GC pause hoặc resource contention và phản hồi chậm, tool tự động giảm tốc độ gửi request.
Kết quả: khoảng thời gian server chậm nhất lại có ít request nhất trong dataset → toàn bộ giai đoạn chậm bị underrepresented trong phân phối latency. P99 đo được sẽ thấp hơn đáng kể so với thực tế.
Trong ví dụ trên, khi server mất 500ms để xử lý Request 2 (do GC pause), tool đã không gửi ~50 request mà lẽ ra phải gửi (nếu duy trì rate 100 req/s). Những request bị thiếu đó — với latency thực tế rất cao — hoàn toàn biến mất khỏi kết quả đo.
Closed-loop vs Open-loop
Trong hệ thống production thực tế, user request đến theo open-loop: người dùng không chờ request của người khác hoàn thành rồi mới gửi. Vì vậy, load test cần mô phỏng đúng hành vi này bằng constant-rate loading.
Giải pháp: Constant-Rate Loading
Để tránh Coordinated Omission, cần sử dụng các tool hỗ trợ open-loop model — gửi request theo một schedule cố định bất kể server response nhanh hay chậm.
# wrk2: constant-rate mode (1000 req/s trong 60 giây)
wrk2 -t4 -c100 -d60s -R1000 http://api.example.com
# k6: arrival-rate executor
# config trong k6 script:
export const options = {
scenarios: {
constant_load: {
executor: 'constant-arrival-rate',
rate: 1000, // 1000 iterations/s
timeUnit: '1s',
duration: '60s',
preAllocatedVUs: 200,
maxVUs: 500,
},
},
};
export default function() {
http.get('http://api.example.com');
}
# Gatling: open-model injection
setUp(
scn.inject(
constantUsersPerSec(1000).during(60)
)
)So sánh các tool
| Tool | Mode | Coordinated Omission |
|---|---|---|
ab, wrk | Closed-loop | ❌ Bị ảnh hưởng |
wrk2 | Constant-rate | ✅ Tránh được |
k6 (constant-arrival-rate) | Open-loop | ✅ Tránh được |
Gatling (open-model) | Open-loop | ✅ Tránh được |
JMeter (Constant Throughput Timer) | Semi-open | ⚠️ Phụ thuộc cấu hình |
Lưu ý: wrk2 là phiên bản cải tiến của wrk, được Gil Tene đóng góp phần constant-rate. Flag -R (rate) là điểm khác biệt quan trọng nhất.
Không trung bình hóa Percentile
Một sai lầm cực kỳ phổ biến: tính trung bình của các percentile rồi coi đó là percentile tổng hợp. Cách làm này sai về mặt toán học.
Ví dụ sai lầm
| Server | P99 |
|---|---|
| Server A | 80ms |
| Server B | 120ms |
| Server C | 300ms |
❌ SAI: "P99 trung bình = (80 + 120 + 300) / 3 = 167ms"
✅ ĐÚNG: Merge toàn bộ raw data từ cả 3 server lại, rồi tính P99 trên tập dữ liệu gộp. Giá trị thực có thể cao hơn hoặc thấp hơn 167ms đáng kể — bạn không biết được cho đến khi merge.
Tại sao sai?
Percentile không phải phép đo additive (cộng được). Chúng là order statistic — phụ thuộc vào vị trí trong phân phối, không phải giá trị tuyệt đối. Khi trung bình hóa percentile, bạn mất thông tin về phân phối đuôi (tail distribution) của từng server, và kết quả có thể underestimate hoặc overestimate tail latency thực tế.
Quy tắc vàng: Never average percentiles. Merge first, compute after.
Tìm hiểu thêm tại Latency Percentile Metrics.
Các sai lầm đo lường khác
1. Không đủ samples
Để đo percentile Pxx, bạn cần tối thiểu đủ samples để có ý nghĩa thống kê. P99 với chỉ 50 samples nghĩa là bạn chỉ có 0.5 data point ở tail — kết quả hoàn toàn vô nghĩa.
Khuyến nghị: sử dụng ít nhất 10x so với mức tối thiểu để có kết quả ổn định. Với P99.9, cần ít nhất 10,000 samples — tốt nhất là 100,000+.
2. Đo phía server thay vì phía client
Đo latency ở server-side bỏ qua các thành phần quan trọng mà user thực sự trải nghiệm:
- Network latency — round-trip giữa client và server
- Client-side queuing — thời gian request chờ trong connection pool
- Load balancer processing — thời gian xử lý tại LB/reverse proxy
- TLS handshake — đặc biệt đáng kể với cold connection
Luôn đo từ phía client (hoặc tốt hơn: từ vị trí gần nhất với end-user) để phản ánh trải nghiệm thực tế.
3. Bỏ qua latency của failed requests
Khi request bị timeout (ví dụ: 30s timeout) hoặc trả về 5xx, latency của chúng thường rất cao nhưng lại bị loại khỏi dataset hoặc ghi nhận là 0ms. Đây là một dạng Coordinated Omission ở mức data processing — bạn đang loại bỏ chính xác những data point tệ nhất.
Quy tắc: Mọi request — kể cả timeout và error — đều phải được tính vào phân phối latency với latency thực tế của chúng.
Xem thêm về tác động ở quy mô lớn tại Tail Latency at Scale.
Checklist Load Test chính xác
Áp dụng checklist sau để đảm bảo kết quả load test phản ánh đúng thực tế:
| ✓ | Hạng mục | Chi tiết |
|---|---|---|
| ☐ | Constant-rate tool | Sử dụng wrk2, k6 (constant-arrival-rate), hoặc Gatling (open-model) |
| ☐ | Đủ samples | ≥1,000 cho P99, ≥10,000 cho P99.9 |
| ☐ | Multi-level load | Test ở 50%, 70%, 90%, 100%, 120% target throughput |
| ☐ | Latency + Throughput đồng thời | Đo cả hai để xác định saturation point — điểm mà latency bắt đầu tăng phi tuyến |
| ☐ | So sánh P50 vs P99 | Nếu gap > 10x → cần điều tra nguyên nhân tail latency (GC, lock contention, noisy neighbor...) |
| ☐ | Tính cả failed requests | Timeout và 5xx đều phải có trong phân phối latency |
| ☐ | Đo từ client-side | Bao gồm network, queuing, TLS overhead |
| ☐ | Không average percentile | Merge raw data trước, tính percentile sau |
Tham khảo thêm: Latency Percentile Metrics | Adaptive Concurrency Control