System Design

Idempotency key: retry an toàn cho thanh toán, API và tool của agent

Tóm tắt

Idempotency key biến gửi lại có thể gây trùng thành gửi lại luôn an toàn: một khoá cho một thao tác, server lưu và trả lại kết quả cũ. Kết hợp retry với backoff và jitter, và áp dụng cho mọi tool có tác dụng phụ mà agent gọi.

Client gửi request "tạo thanh toán 1 triệu đồng", mạng chập chờn, không nhận được phản hồi. Gửi lại thì sợ trừ tiền hai lần; không gửi lại thì không biết giao dịch đã thành công chưa. Đây là bài toán kinh điển của hệ thống phân tán, và lời giải chuẩn là idempotency key. Stripe phổ biến cách làm này, và IETF đang chuẩn hoá thành header Idempotency-Key. Với agent AI hay gọi tool rồi retry, khái niệm này càng quan trọng.

Idempotent là gì?

Một thao tác là idempotent nếu thực hiện nhiều lần cho kết quả như thực hiện một lần. Theo chuẩn HTTP, GET, PUT, DELETE được định nghĩa là idempotent; POST (tạo mới) thì không, nên cần cơ chế bổ sung.

Cơ chế idempotency key

  1. Client sinh một khoá duy nhất (thường là UUID v4) cho mỗi thao tác logic.
  2. Gửi kèm header: Idempotency-Key: 5f1c….
  3. Server lưu khoá cùng kết quả xử lý lần đầu.
  4. Request lặp lại với cùng khoá → server trả lại đúng kết quả đã lưu, không xử lý lần nữa.
POST /v1/payments
Idempotency-Key: 5f1c2a7e-9d3b-4c61-a8e2-0b7f3c9d1e44
Content-Type: application/json

{"amount": 1000000, "currency": "vnd"}

Các tình huống server phải xử lý

Tình huốngPhản hồi hợp lý
Khoá mớiXử lý bình thường, lưu kết quả
Khoá đã có, đã xử lý xongTrả lại kết quả đã lưu
Khoá đang được xử lý (request song song)Báo xung đột (bản nháp IETF gợi ý 409), client thử lại sau
Cùng khoá nhưng nội dung request khácTừ chối (bản nháp IETF gợi ý 422), vì đó là lỗi của client
Endpoint bắt buộc khoá nhưng thiếu400 Bad Request

Khoá nên có thời hạn (Stripe giữ khoảng 24 giờ) để kho lưu không phình mãi.

Kết hợp với retry

Idempotency key là thứ cho phép retry an toàn. Bài viết của Stripe nhấn mạnh hai mảnh đi cùng: retry với exponential backoff và jitter (độ trễ ngẫu nhiên) để hàng nghìn client không retry cùng lúc làm sập server.

Vì sao quan trọng với agent AI?

  • Agent hay gọi lại tool khi timeout hoặc khi "nghĩ" lần trước chưa thành công.
  • Tool có tác dụng phụ (gửi email, tạo đơn, chuyển tiền) phải nhận idempotency key; agent framework nên sinh khoá theo mỗi lần quyết định hành động, không theo mỗi lần retry.

Rủi ro và lưu ý

  • Sinh khoá mới mỗi lần retry: lỗi phổ biến nhất, làm mất toàn bộ tác dụng. Khoá gắn với thao tác, không gắn với lần gửi.
  • Lưu kết quả không nguyên tử: phải ghi khoá và kết quả trong cùng giao dịch với thao tác chính.
  • Lưu cả lỗi tạm thời: cân nhắc có lưu lỗi 500 hay cho phép thử lại; Stripe không lưu kết quả khi request chưa thực sự được xử lý.

Áp dụng thế nào?

  • Dev: Mọi endpoint POST có tác dụng phụ quan trọng đều nhận Idempotency-Key; client sinh khoá một lần cho mỗi thao tác.
  • Tech Lead: Đưa idempotency vào chuẩn thiết kế API và vào mọi tool cho agent có hành động thật.
  • PM: Với thanh toán và đơn hàng, đây là yêu cầu bắt buộc, không phải tối ưu.

Nguồn & tham khảo

  1. Designing robust and predictable APIs with idempotency — Stripe Blog
  2. The Idempotency-Key HTTP Header Field — IETF HTTPAPI WG