Dependency vừa sống lại sau 2s. 400 pod cùng
retry() ngay → 400 × N request đập một lúc. Dependency
gục lại. Bạn không “chữa blip” — bạn
tự tổ chức một cuộc tấn công.
Bài retry + backoff + jitter nói cách (nhịp, jitter, max). Bài này nói có nên bấm — phân loại lỗi, và khi bấm là tự hại.
Demo nhịp storm (đã có): retry storm vs backoff + jitter.
Ba loại lỗi (trước khi nghĩ backoff)
Transient — thử lại có hy vọng: blip mạng, 429/503, leader election ngắn
Permanent — thử lại vô ích / có hại: 400 validation, 401 sai secret, 404 “không có”
Unknown — không biết effect đã xảy ra chưa: timeout giữa chừng, connection reset sau write
Backoff chỉ có ích với transient (và unknown khi đã idempotent). Permanent + retry = đốt budget và che bug.
Thường được retry
- Connect reset, DNS tạm, TLS handshake fail (tùy lần).
-
HTTP 408, 429 (tôn
Retry-After), 502/503/504 — nếu doc/service nói safe. - GET và thao tác đọc; POST/PUT có idempotency key hoặc semantics idempotent (PUT cùng body).
- Còn budget thời gian và breaker không đỏ.
Thường không retry
- 400 schema/validation — sửa request, không spam.
- 401/403 (trừ flow refresh token có chủ đích).
- 404 “resource không tồn tại” (khác 404 “routing tạm”).
- 409 business conflict — cần logic, không phải thử lại mù.
- Side-effect không idempotent: charge thẻ, gửi SMS OTP, “tạo đơn” không key.
- Budget = 0; client đã cancel.
Unknown / timeout — bẫy double
Server có thể đã commit trước khi network cắt. Retry không key = hai lần effect. Đây là uncertain, không phải transient thuần.
- Idempotency key / tra cứu trạng thái trước khi “thử lại cho chắc”.
- At-least-once + idempotent = exactly-once thực dụng.
Năm cách retry tự đấm mình
1) Thundering herd / storm
Hết blip → mọi client đập cùng lúc (thiếu jitter, thiếu breaker)
2) Amplification đa tầng
Client ×3, API ×3, service ×3 → 27 lần gọi dependency cho 1 ý định
3) Poison loop (queue)
Message permanent fail, retry mãi → chặn partition / consumer
→ cần DLQ / unblock, không phải backoff vô hạn
4) Budget burn + orphan
Retry khi user cút; attempt cũ không cancel → pool đầy xác
5) Deploy / scale herd
Mọi instance ready cùng lúc, warm cache/DB cùng một pattern retry
Chữa storm: jitter + cap + breaker + load shedding phía dependency. Chữa poison: unblock + DLQ.
Amplification — tính trên giấy 30 giây
User 1 click
→ API retry 3
→ Service retry 3
→ DB timeout chậm
Worst: 1 × 3 × 3 = 9 (hoặc hơn nếu mỗi hop full budget)
Cộng 500 user simultaneous → dependency nhận hàng nghìn
Policy hay dùng: retry ở một tầng (edge hoặc client), tầng trong fail-fast + breaker; hoặc budget chung không cho hop trong retry khi gần hết giờ.
Checklist 15 giây trước khi bọc retry
- Lỗi transient / permanent / unknown?
- Thao tác idempotent (tự nhiên hoặc nhờ key)?
- Còn timeout budget? Attempt trước đã cancel chưa?
- Breaker / 503 Retry-After?
- Fan-out × max attempts có nổ dependency không?
- Queue: max retry + DLQ chưa, hay loop mãi?
Đọc tiếp
- Backoff + jitter — nhịp đúng khi đã quyết định retry.
- Circuit breaker — ngừng gọi khi đỏ.
- Idempotency key scope — key sai scope thì retry vẫn double.
Một câu để nhớ
Retry là vũ khí — có hướng nổ ngược.
Backoff chỉ là nhịp cò; chọn mục tiêu sai vẫn bắn vào chân.