Timeout budget trong code: một deadline cho cả chuỗi. Khi p99 đỏ, log “timeout” không nói hop nào ăn hết ms.
Distributed trace (OpenTelemetry, Jaeger, Zipkin…) là kính hiển vi: mỗi hop một span. Bài này: đọc trace bằng mắt budget — không phải tutorial cài agent.
Budget trên giấy vs trên trace
Code: deadline = now + 2000ms, truyền xuống
Trace: thấy thực tế
API 0───1950ms─────────────────────x timeout
├ pool.wait 0──800ms──┐
├ db.query 800──1400
└ http.B 1400──1900 (retry ×2?)
Cùng “timeout 2s”, root cause có thể là acquire pool, query, hay retry amplification — ba hướng chữa khác nhau.
Năm pattern đọc nhanh
- Parent ≫ sum(children): thời gian “mất tích” — queue nội bộ, GC, sync trên event loop, serialization.
- Span pool.acquire / getConnection dài: pool exhaustion — CPU có thể rảnh.
- Nhiều span con giống nhau liên tiếp: retry / hedge — đếm attempt attribute.
- Span status error + message canceled/aborted: cancel đúng hay timeout không abort? cancellation.
- Deadline còn 0 mà vẫn start child: budget không xuyên tầng — bug policy.
Attribute nên có
deadline_ms / deadline_remaining_ms
attempt / retry_reason
outcome = ok | timeout | aborted | error
http.route / rpc.method
pool.wait_ms (nếu instrument được)
Không có remaining_ms vẫn suy từ start time vs deadline header — instrument sẵn thì on-call đỡ đoán.
Checklist 1 trace chậm
- Root duration vs SLA/budget?
- Critical path span nào dài nhất?
- Chờ (queue/pool) hay làm (query/CPU)?
- Có retry/hedge không — tổng work vs 1 attempt?
- Client đã abort chưa — server còn chạy orphan?
Giới hạn
Trace không thay budget trong code; sampling có thể bỏ đúng request xấu; span thiếu = mù. Vẫn cần metric in-flight / pool.
Một câu
Trace là kính hiển vi của budget — chỉ ra hop nào ăn deadline, không thay việc truyền deadline xuống.