Part 05 · Reliability · 5.1.04

Error contract và idempotency

Error response phải giúp client quyết định hành động an toàn; idempotency phải bảo vệ business side effect xuyên concurrent duplicate, timeout và crash window.

Không nhầm: idempotency làm replay an toàn hơn, nhưng không thay thế authorization, input validation, transaction boundary hay reconciliation với downstream.

1. Problem Details và taxonomy

RFC 9457 định nghĩa type (loại problem), title (summary ổn định), status (HTTP status mang tính advisory), detail (giải thích occurrence) và instance (định danh occurrence). Extension có thể thêm machine code, field violations hoặc retry metadata. Client nên branch theo type/code, không parse human message.

{"type":"https://api.example/problems/conflict",
 "title":"Resource state conflict","status":409,
 "code":"ORDER_VERSION_MISMATCH","traceId":"req-7f3a",
 "violations":[{"field":"version","expected":7}]}
ClassMapping / client action
Validation400/422; sửa payload, không retry mù
Authentication/authorization401/403; refresh/login hoặc báo denied, không retry liên tục
Conflict/precondition409/412; đọc state mới, merge hoặc yêu cầu user decision
Quota/transient dependency429/502/503/504; retry bounded khi policy cho phép
Internal500; safe message + trace ID, log chi tiết ở server

Không trả 200 với success:false cho lỗi HTTP; không leak stack trace, SQL, token, secret hay topology.

2. Idempotency key và atomic ownership

Scope key tối thiểu là tenant + operation + key. Server lưu request fingerprint để cùng key nhưng payload khác bị reject conflict. Unique constraint hoặc atomic insert chọn một owner; concurrent duplicate phải chờ/poll, trả processing state hoặc replay outcome đã lưu.

BEGIN
  INSERT idempotency(tenant, operation, key, fingerprint, state)
  VALUES (...)                         -- unique owner
  ON CONFLICT (tenant, operation, key) DO NOTHING;
  -- owner executes; duplicate reads PROCESSING/SUCCEEDED/FAILED_RETRYABLE
COMMIT

Response snapshot cần gắn status và headers cần replay. Fingerprint phải canonicalize có chủ đích (không để thứ tự JSON vô nghĩa tạo conflict giả), nhưng không bỏ qua field ảnh hưởng business.

3. Crash windows và dual-write

Nếu gateway charge thành công nhưng process chết trước khi lưu result, local idempotency record không đủ. Truyền cùng stable key tới provider nếu provider hỗ trợ; nếu outcome unknown, chuyển state UNKNOWN, reconcile qua query/webhook và audit thay vì charge lại mù. Outbox/state machine giúp phối hợp DB với side effect nhưng không biến hệ thống thành distributed transaction tự động.

  1. Reserve operation và validate authorization/invariant.
  2. Gọi downstream với key ổn định, ghi attempt/timeout.
  3. Lưu outcome durable; nếu crash, worker reconciliation tiếp tục.
  4. Replay đúng outcome hoặc trạng thái đang xử lý cho duplicate.

4. Retention, security và retry

TTL phải dài hơn retry window và thời gian business reconciliation; cleanup không xóa key khi operation còn có thể replay. Key cần entropy đủ cao để chống đoán/collision. Response snapshot có PII cần encryption, access control và retention policy.

5. Interview checklist

Tài liệu: RFC 9457 Problem Details · RFC 9110 HTTP Semantics · Stripe idempotent requests (pattern reference)