Preview Bản nháp chờ review — title, nội dung, link có thể sửa trước khi bỏ nhãn.

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

  1. Lỗi transient / permanent / unknown?
  2. Thao tác idempotent (tự nhiên hoặc nhờ key)?
  3. Còn timeout budget? Attempt trước đã cancel chưa?
  4. Breaker / 503 Retry-After?
  5. Fan-out × max attempts có nổ dependency không?
  6. Queue: max retry + DLQ chưa, hay loop mãi?

Đọc tiếp

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.