aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

Idempotency-Key cho API tạo đơn: retry mà không tạo trùng nghiệp vụ

Suong Nguyen by Suong Nguyen
22/10/2026
in Backend
0
585
SHARES
3.2k
VIEWS
Summarize with ChatGPTShare to Facebook

Client gửi lệnh tạo đơn, server lưu thành công, nhưng kết nối đứt trước khi response về máy. Người dùng nhấn lại hoặc ứng dụng retry. Nếu lần gửi tiếp theo bị hiểu thành một nghiệp vụ mới, hệ thống có thể tạo hai đơn cho cùng một ý định mua hàng.

Tắt nút sau lần nhấn đầu giúp giao diện dễ dùng hơn, nhưng không giải quyết request lặp từ mạng, worker hay nhiều thiết bị. Bài viết trình bày một thiết kế minh họa với PostgreSQL 17 và HTTP, đối chiếu tài liệu ngày 22/10/2026. Đây chưa phải endpoint hoàn chỉnh đã kiểm thử trên hệ thống của bạn.

Xác định “cùng một yêu cầu”

Client tạo một khóa ngẫu nhiên cho một ý định tạo đơn. Khi retry vì chưa biết kết quả, client giữ nguyên khóa và payload. Khi người dùng bắt đầu một ý định mới, client dùng khóa mới. Nếu mỗi lần retry lại sinh UUID mới thì backend không có dấu hiệu để nhận biết chúng thuộc cùng một thao tác.

Ví dụ hợp đồng:

POST /orders
Idempotency-Key: 7fe3fcb0-4f6b-4eac-b780-35432afc2a17
Content-Type: application/json

{"items":[{"sku":"BOOK-01","quantity":1}]}

Header này chỉ có tác dụng khi server triển khai hợp đồng tương ứng. Một API tự nhận header nhưng không kiểm tra hoặc lưu gì vẫn có thể tạo trùng. Theo HTTP, tính idempotent nói về hiệu ứng dự định của các request giống nhau; một lệnh POST tạo đơn không tự có đặc tính đó chỉ vì được gửi qua HTTP.

Phạm vi khóa quan trọng hơn vẻ ngẫu nhiên

Đừng dùng khóa làm bằng chứng xác thực. Hãy tính scope từ tài khoản hoặc tenant đã xác thực và thao tác đang thực hiện. Ví dụ, khóa của tenant A tại POST /orders không nên tra ra response của tenant B.

Sau validation, tạo fingerprint từ dữ liệu nghiệp vụ đã chuẩn hóa. Nếu cùng scope và khóa nhưng fingerprint khác, trả lỗi theo hợp đồng, chẳng hạn 409 Conflict, thay vì âm thầm dùng đơn cũ. Cần quy định rõ cách xử lý thứ tự phần tử, giá trị mặc định và những trường do server tính; băm chuỗi JSON tùy ý dễ tạo ra các trường hợp khó giải thích.

Một bảng lưu kết quả tối giản:

CREATE TABLE idempotency_results (
  scope text NOT NULL,
  key text NOT NULL,
  request_hash text NOT NULL,
  response_status integer,
  response_body jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (scope, key)
);

Đây là schema minh họa. Khi triển khai cần bổ sung giới hạn độ dài, thời gian lưu, giám sát dung lượng và chính sách bảo vệ dữ liệu trong response. Không đưa email hay thông tin định danh nhạy cảm vào chính khóa để tiện tra cứu.

Gắn bản ghi nghiệp vụ với kết quả trong cùng transaction

Với nghiệp vụ chỉ ghi trong một database, có thể đặt việc nhận khóa, tạo đơn và lưu response vào cùng transaction. Bước nhận khóa có dạng:

INSERT INTO idempotency_results(scope, key, request_hash)
VALUES ($1, $2, $3)
ON CONFLICT (scope, key) DO NOTHING
RETURNING key;

Nếu có dòng trả về, transaction này đã nhận khóa. Nó tạo đơn, lưu status/body sẽ trả cho client rồi commit. Không commit bản ghi khóa đang dang dở trước khi tạo đơn nếu thiết kế chưa có cơ chế quản lý trạng thái processing và phục hồi tương ứng.

Nếu không có dòng trả về, đọc kết quả của cùng scope và khóa. So fingerprint trước khi replay status/body. Unique constraint là điểm phối hợp giữa các worker; thao tác “SELECT xem chưa có, rồi INSERT” tách rời không tự chặn được hai request đồng thời.

Trong mức cô lập mặc định Read Committed, xung đột khóa có thể khiến INSERT chờ transaction trước hoàn tất. Hãy dùng câu truy vấn tiếp theo để đọc kết quả đã commit, đặt timeout và xử lý lỗi transaction rõ ràng. Đừng giả định mọi response trùng sẽ trả ngay lập tức.

Khoảng trống khi có dịch vụ bên ngoài

Một transaction PostgreSQL không thể rollback email đã gửi hoặc khoản thu đã được nhà cung cấp thanh toán chấp nhận. Vì thế không nên nhét thao tác gọi mạng vào giữa rồi xem rollback database là cơ chế hoàn tác toàn bộ.

Với tác vụ nền, cân nhắc ghi một sự kiện outbox cùng transaction tạo đơn, rồi để worker xử lý sau. Worker và dịch vụ nhận sự kiện vẫn cần cơ chế chống xử lý trùng phù hợp. Nếu provider có idempotency riêng, hãy đọc hợp đồng của đúng API và lưu mối liên hệ với nghiệp vụ nội bộ.

Stripe là một ví dụ cụ thể, không phải tiêu chuẩn chung cho mọi backend. Tài liệu hiện phân biệt API v1 và API v2; với v1, kết quả đã lưu có thể được trả lại cả khi status là 500. Không áp dụng nguyên xi quy tắc đó cho API của bạn mà không giải thích cho client.

Các tình huống cần kiểm thử

  • Cùng khóa và payload gửi lần lượt: chỉ có một đơn, kết quả được replay.
  • Cùng khóa nhưng payload khác: lỗi rõ ràng, không thay đổi đơn cũ.
  • Hai request cùng đến: unique constraint phối hợp được, không có hai đơn.
  • Mất response sau commit: retry tìm lại kết quả đã lưu.
  • Tiến trình chết trước commit: transaction không để lại đơn đã commit mà thiếu kết quả tương ứng.
  • Khóa hết hạn: client biết rằng dùng lại khóa sau thời gian lưu có thể trở thành thao tác mới.

Các trường hợp trên là kế hoạch kiểm thử đề xuất, không phải báo cáo benchmark hay kết quả thử tải. Khi cần đánh giá thực tế, quan sát cả số bản ghi nghiệp vụ và phản hồi API; chỉ so hai response giống nhau chưa đủ chứng minh không có tác động phụ lặp.

Kết luận

Idempotency là một hợp đồng giữa ý định của client, phạm vi phân quyền và nơi backend lưu kết quả. Hãy bắt đầu từ transaction và tình huống mất response, rồi mở rộng sang worker hoặc provider khi đã xác định rõ ranh giới của từng hệ thống.

Nguồn tham khảo

  • RFC 9110: Idempotent Methods
  • PostgreSQL 17: INSERT và ON CONFLICT
  • PostgreSQL 17: Transaction Isolation
  • Stripe: Idempotent requests, phân biệt API v1 và v2

Tags: APIIdempotencyPostgreSQL
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

Phân trang keyset trong PostgreSQL bằng created_at và id

by Suong Nguyen
21/10/2026
0

Dùng cặp created_at và id làm mốc phân trang, tránh thứ tự không xác định và hiểu giới hạn của keyset khi dữ liệu thay đổi.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

Thiết kế backend API gọn gàng cho sản phẩm nhỏ

by admin
02/07/2026
0

Một backend API tốt cho sản phẩm nhỏ không cần quá phức tạp, nhưng phải rõ resource, validate kỹ, trả lỗi nhất quán và dễ mở rộng.

Minh họa AI hỗ trợ lập trình viên xây dựng sản phẩm phần mềm

AI đang thay đổi cách lập trình viên xây dựng sản phẩm

by admin
02/07/2026
0

AI không thay thế lập trình viên ngay lập tức, nhưng đang thay đổi cách chúng ta phân tích yêu cầu, viết code, review và vận hành sản...

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native: tránh bàn phím che form với KeyboardAvoidingView và ScrollView
  • Phỏng vấn JavaScript: giải thích event loop, microtask và async/await
  • Deep link mobile an toàn: đúng domain chưa đủ, còn route và quyền đọc

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply