Idempotency là gì? Cách thiết kế API chống double submit và retry lỗi

Idempotency giúp backend xử lý retry, double click và timeout mà không tạo đơn hàng, giao dịch hoặc side effect bị nhân đôi. Bài viết giải thích cách thiết kế idempotency key, lưu trạng thái request và vận hành an toàn trong production.

Idempotency là gì? Cách thiết kế API chống double submit và retry lỗi
Idempotency là gì? Thiết kế API chống double submit và...

Idempotency là gì? Cách thiết kế API chống double submit và retry lỗi

Idempotency giúp backend xử lý retry, double click và timeout mà không tạo đơn hàng, giao dịch hoặc side effect bị nhân đôi. Bài viết giải thích cách thiết kế idempotency key, lưu trạng thái request và vận hành an toàn trong production.

Idempotency là gì? Cách thiết kế API chống double submit và retry lỗi

Idempotency là cơ chế giúp một request có thể được gửi nhiều lần nhưng hệ thống chỉ tạo ra một kết quả logic duy nhất. Với backend production, đây không phải chi tiết “nice to have”. Nó là lớp bảo vệ quan trọng cho các API tạo đơn hàng, thanh toán, nạp tiền, gửi email, tạo ticket, hoặc bất kỳ thao tác nào có side effect.

Vấn đề thực tế thường bắt đầu rất bình thường: người dùng bấm nút hai lần, mobile app mất mạng rồi retry, gateway timeout nhưng server vẫn xử lý xong, hoặc job queue chạy lại sau khi worker chết giữa chừng. Nếu API không idempotent, cùng một hành động có thể tạo hai đơn hàng, trừ tiền hai lần, hoặc gửi nhiều email giống nhau.

Sơ đồ luồng idempotency key giúp backend API chống double submit và retry lỗi
Idempotency key giúp backend nhận ra retry hợp lệ và trả lại kết quả cũ thay vì chạy side effect lần nữa.

Idempotency là gì trong backend API?

Một operation được gọi là idempotent khi chạy một lần hay nhiều lần vẫn tạo ra cùng một trạng thái cuối. Trong HTTP, GET, PUT, DELETE thường được kỳ vọng idempotent theo ngữ nghĩa. Nhưng các endpoint POST /orders, POST /payments, POST /withdrawals lại không tự nhiên idempotent, vì mỗi lần gọi thường tạo một resource hoặc side effect mới.

Để biến các endpoint kiểu này thành an toàn khi retry, client gửi thêm một Idempotency-Key. Backend dùng key đó như dấu vân tay của một ý định nghiệp vụ: “tạo đơn hàng này một lần”. Nếu request bị gửi lại với cùng key, backend không tạo thêm đơn mới mà trả về kết quả đã lưu trước đó.

Vì sao retry dễ gây lỗi production?

Trong môi trường thật, timeout không có nghĩa là request thất bại. Có thể API gateway trả timeout ở giây thứ 30, nhưng service phía sau vẫn commit database ở giây thứ 31. Client không biết điều đó, nên retry. Nếu backend chỉ nhìn request như một lệnh mới, side effect sẽ bị nhân đôi.

Các nguồn retry phổ biến gồm mobile network chập chờn, browser double submit, payment gateway retry webhook, message broker redelivery, worker crash trước khi ack, hoặc circuit breaker mở rồi đóng lại. Vì vậy, retry policy chỉ an toàn khi đi kèm idempotency strategy.

Thiết kế Idempotency-Key thế nào?

Key nên do client sinh ra cho từng ý định nghiệp vụ, ví dụ một UUID v4 cho thao tác checkout. Backend cần scope key theo user, tenant, endpoint hoặc business action. Không nên dùng key global đơn giản, vì hai user khác nhau có thể vô tình gửi cùng giá trị.

Một bản ghi idempotency thường cần các trường: key, user_id, route, request_hash, status, response_body, resource_id, expires_at. Quan trọng nhất là unique constraint ở database, ví dụ unique trên (user_id, route, key). Nếu chỉ check rồi insert ở application layer, race condition vẫn có thể tạo duplicate.

State machine xử lý idempotent request trong backend production
State machine giúp quyết định request retry nên được xử lý tiếp, trả kết quả cũ, hay bị từ chối vì payload khác.

Khi retry thì trả gì?

Cách an toàn nhất là lưu response thành công đầu tiên và trả lại response đó cho các retry cùng key. Nếu request đầu tiên đang xử lý, backend có thể trả 202 Accepted, 409 Conflict, hoặc hướng client polling theo resource ID. Chọn mã nào phụ thuộc vào UX và loại API, nhưng phải nhất quán.

Nếu retry dùng cùng key nhưng payload khác, backend nên từ chối bằng 400 hoặc 409. Đây là lý do cần lưu request_hash. Idempotency key không được trở thành lối tắt để một key đại diện cho nhiều ý định khác nhau.

Pseudo-code cho endpoint tạo đơn hàng

POST /orders
Idempotency-Key: 7f6b9a9e-...

begin transaction
  record = find_or_create_idempotency_key(user, route, key)

  if record.completed?
    return record.saved_response

  if record.processing? and record.locked_by_other_request?
    return 409, { error: "request_is_processing" }

  ensure request_hash == record.request_hash
  mark record as processing
  order = create_order_once(params)
  save response into record
  mark record as completed
commit

return order_response

Trong triển khai thật, phần khó không nằm ở vài dòng pseudo-code mà ở transaction boundary. Nếu tạo order thành công nhưng lưu idempotency response thất bại, retry sau đó vẫn có thể tạo thêm order. Vì vậy, side effect chính và idempotency record nên nằm trong cùng transaction nếu có thể. Với side effect ngoài database như gửi email hoặc gọi payment provider, cần thêm outbox pattern hoặc provider-side idempotency.

TTL nên đặt bao lâu?

Không nên giữ idempotency key vô hạn. TTL phụ thuộc vào nghiệp vụ: checkout có thể giữ vài giờ đến vài ngày; payment hoặc ledger có thể cần lâu hơn; webhook từ bên thứ ba nên theo retry window của provider. Tuy nhiên, nếu TTL quá ngắn, retry trễ có thể vượt qua lớp bảo vệ và tạo duplicate.

Với dữ liệu nhạy cảm, không nhất thiết phải lưu toàn bộ response body. Có thể lưu resource ID và reconstruct response khi retry. Nhưng cần đảm bảo resource đó vẫn truy xuất được và response không làm lộ dữ liệu của tenant khác.

Checklist production

Checklist triển khai idempotency cho API thanh toán và tạo đơn hàng
Checklist idempotency nên được review trước khi mở retry tự động ở client, gateway hoặc worker.
  • Yêu cầu Idempotency-Key cho các endpoint tạo side effect quan trọng.
  • Scope key theo user/tenant/route để tránh đụng key ngoài ý muốn.
  • Dùng unique constraint hoặc distributed lock đúng cách, không chỉ check ở memory.
  • Lưu request hash để phát hiện retry cùng key nhưng khác payload.
  • Lưu trạng thái processing/completed/failed rõ ràng.
  • Đảm bảo transaction không tạo resource thành công nhưng mất idempotency record.
  • Định nghĩa TTL theo retry window thực tế.
  • Log idempotency_key, request_id, resource_id để debug incident.
  • Viết test cho double submit, concurrent request, timeout-after-commit và worker retry.

Kết luận

Idempotency là một trong những khác biệt rõ nhất giữa API chạy demo và API chịu được production. Khi hệ thống bắt đầu có payment, order, workflow automation hoặc message queue, câu hỏi không còn là “có retry không”, mà là “retry có an toàn không”.

Nếu team của bạn đang thiết kế backend API, hãy đọc thêm REST API Design Checklist, Authentication và Authorization trong BackendDeploy Backend lên Production Checklist để hoàn thiện chuỗi thiết kế API, bảo mật và vận hành.

Muốn học Engineering có mentor hỗ trợ?

Khoá học PSE của Cole.vn đào tạo bạn tư duy Engineering thực chiến — nền tảng vững để bứt phá sự nghiệp.

Tìm hiểu khoá PSE →
Software Engineer Academy
Tác giả
Software Engineer Academy
Đội ngũ biên tập Software Engineer Academy

Chúng tôi chia sẻ kiến thức Software Engineering thực chiến — từ nền tảng coding đến mindset career, được chắt lọc từ kinh nghiệm xây dựng sản phẩm và đào tạo hàng trăm học viên.

🔥 Chỉ còn 20 suất — Khai giảng khoá PSE gần nhất

Bắt đầu hành trình thành Software Engineer hôm nay

Đăng ký để đội ngũ Cole.vn liên hệ tư vấn miễn phí trong vòng 24 giờ.

Chúng tôi không spam. Thông tin của bạn được bảo mật tuyệt đối.

Muốn học Engineering có mentor hỗ trợ?

Khoá học PSE của Cole.vn đào tạo bạn tư duy Engineering thực chiến — nền tảng vững để bứt phá sự nghiệp.

Tìm hiểu khoá PSE →
Software Engineer Academy
Tác giả
Software Engineer Academy
Đội ngũ biên tập Software Engineer Academy

Chúng tôi chia sẻ kiến thức Software Engineering thực chiến — từ nền tảng coding đến mindset career, được chắt lọc từ kinh nghiệm xây dựng sản phẩm và đào tạo hàng trăm học viên.

🔥 Chỉ còn 20 suất — Khai giảng khoá PSE gần nhất

Bắt đầu hành trình thành Software Engineer hôm nay

Đăng ký để đội ngũ Cole.vn liên hệ tư vấn miễn phí trong vòng 24 giờ.

Chúng tôi không spam. Thông tin của bạn được bảo mật tuyệt đối.