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.
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}]}
| Class | Mapping / client action |
|---|---|
| Validation | 400/422; sửa payload, không retry mù |
| Authentication/authorization | 401/403; refresh/login hoặc báo denied, không retry liên tục |
| Conflict/precondition | 409/412; đọc state mới, merge hoặc yêu cầu user decision |
| Quota/transient dependency | 429/502/503/504; retry bounded khi policy cho phép |
| Internal | 500; 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.
- Reserve operation và validate authorization/invariant.
- Gọi downstream với key ổn định, ghi attempt/timeout.
- Lưu outcome durable; nếu crash, worker reconciliation tiếp tục.
- 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.
Retry-Afterhữu ích cho 429/503 nhưng không cam kết operation chưa chạy.- Retry chỉ cho transient failure, có backoff+jitter, attempt cap và overall deadline.
- 409/422 thường cần state/payload change; retry y nguyên chỉ tạo load.
5. Interview checklist
- Key scope và unique rule là gì?
- Payload đổi dưới cùng key xử lý thế nào?
- Duplicate concurrent đọc state nào?
- Crash sau side effect nhưng trước response reconcile ra sao?
- TTL, PII, encryption và audit được quyết định bằng threat model nào?