Tại sao ghi file nguyên tử không bảo vệ được ngân sách thử lại trong các tác vụ đồng thời
Một nhà phát triển đã phát hiện ra rằng việc ghi JSON nguyên tử không ngăn chặn được các lệnh gọi API trùng lặp trong một pipeline Python, đòi hỏi phải chuyển sang cơ chế đặt trước và khóa trước khi gọi.
Bài được dịch tự động từ bản gốc tiếng Anh.
Một kỹ sư phần mềm đã phát hiện ra rằng việc sử dụng ghi file nguyên tử trong pipeline xử lý bình luận bằng Python đã không ngăn chặn được các lệnh gọi API bên ngoài trùng lặp khi thực thi đồng thời. Được công bố vào ngày 11 tháng 10 năm 2026, bài phân tích chi tiết cách thức khôi phục sau sự cố và điều kiện tranh chấp (race conditions) đã vượt qua các biện pháp an toàn cục bộ, dẫn đến lãng phí ngân sách thử lại. Tác giả sau đó đã triển khai cơ chế khóa và hệ thống đặt trước trạng thái để áp dụng giới hạn số lần thử nghiêm ngặt trên nhiều điểm vào.
Chuyện gì đã xảy ra
Kỹ sư này duy trì một pipeline gắn kết quả đánh giá của mô hình AI vào các bình luận trên dev.to. Khi mô hình trả về JSON sai định dạng hoặc nhà cung cấp bị quá tải, hệ thống sẽ thử lại yêu cầu bằng cách sử dụng prompt đã được lưu cache. Để kiểm soát chi phí và tải, mỗi bình luận có ngân sách nghiêm ngặt là ba lần thử. Pipeline chạy qua hai điểm vào, một cho Claude và một cho Codex, có thể thực thi cùng mã nguồn chuẩn hóa đồng thời. Ban đầu, nhà phát triển đảm bảo tính toàn vẹn dữ liệu bằng cách sử dụng ghi nguyên tử: lưu vào một file tạm thời rồi thay thế file gốc. Kỹ thuật này đảm bảo rằng người đọc không bao giờ thấy một tài liệu JSON viết dở hoặc bị hỏng.
Bất chấp những biện pháp bảo vệ này, ngân sách thử lại thường xuyên bị vượt quá. Các bài kiểm tra hồi quy sử dụng subprocess thật và một mô hình giả lập đã tiết lộ hai chế độ lỗi cụ thể. Trong kịch bản đầu tiên, mô hình tạo ra phản hồi thành công, nhưng bước tiếp theo để lưu kết quả đánh giá vào đĩa thất bại. Trong trường hợp thứ hai, tiến trình xử lý lệnh gọi mô hình bị kết thúc đột ngột. Trong cả hai trường hợp, một lệnh kiểm tra lại được xếp hàng sẽ đọc từ đĩa, thấy trạng thái không thay đổi so với trước khi thử lại thất bại, và gọi mô hình một lần nữa. Nhật ký hiển thị hai lệnh gọi cho một lần thử lại lẽ ra chỉ nên có một, chứng minh rằng ghi nguyên tử bảo vệ cấu trúc file nhưng không bảo vệ tính nhất quán của trạng thái logic.
Chi tiết chính
- Ghi nguyên tử đảm bảo một file hoặc hoàn chỉnh hoặc vắng mặt, nhưng chúng không theo dõi xem một hành động bên ngoài, chẳng hạn như lệnh gọi API, có thực sự xảy ra hay không.
- Pipeline liên quan đến một chuỗi các bước: đọc trạng thái, gọi mô hình bên ngoài và ghi kết quả vào ba file riêng biệt, những bước này không thể được coi là một giao dịch đơn lẻ.
- Các lỗi xảy ra giữa lệnh gọi mô hình và thao tác lưu cuối cùng xóa sạch mọi bằng chứng cho thấy nỗ lực đó đã diễn ra, khiến các tiến trình sau đó lặp lại công việc.
- Bản sửa lỗi giới thiệu một khóa pipeline chung với thời gian chờ năm giây, đảm bảo chỉ một tiến trình xử lý một bình luận cụ thể tại một thời điểm.
- Số lần thử hiện được đặt trước một cách nguyên tử trong một file hộp thư đến (inbox) trước khi liên hệ với mô hình, vì vậy các nỗ lực bị gián đoạn vẫn được tính vào ngân sách.
- Việc xác thực bao gồm 38 bài kiểm tra đồng thời tập trung và một bộ đầy đủ gồm 223 bài kiểm tra, xác nhận rằng các lỗi được khôi phục không còn kích hoạt các lệnh gọi trùng lặp.
Bối cảnh
Ghi nguyên tử là một chiến lược phổ biến trong lập trình hệ thống để ngăn ngừa hư hỏng dữ liệu. Bằng cách ghi vào một vị trí tạm thời và sau đó đổi tên hoặc di chuyển file đến đích cuối cùng, các nhà phát triển đảm bảo rằng các tiến trình khác không bao giờ đọc một file viết dở. Điều này rất quan trọng đối với các file cấu hình hoặc hồ sơ trạng thái, nơi dữ liệu một phần có thể gây ra sự cố. Tuy nhiên, tính nguyên tử chỉ áp dụng cho chính thao tác file. Nó không cung cấp các đảm bảo giao dịch qua nhiều bước hoặc dịch vụ bên ngoài. Trong các hệ thống phân tán hoặc ứng dụng đồng thời, việc duy trì tính nhất quán thường đòi hỏi phải phối hợp thay đổi trạng thái trên nhiều tài nguyên, điều mà việc thay thế file đơn giản không thể đạt được nếu đứng một mình.
Khi một tiến trình tương tác với các dịch vụ bên ngoài như API AI, chi phí được tính ngay tại thời điểm gửi yêu cầu, chứ không phải khi kết quả được lưu. Nếu một hệ thống gặp sự cố sau khi gửi yêu cầu nhưng trước khi kết quả được lưu vĩnh viễn, dịch vụ bên ngoài đã tính phí cho lệnh gọi đó. Nếu không có cơ chế ghi nhận ý định gọi dịch vụ trước khi lệnh gọi thực sự diễn ra, hệ thống sẽ mất dấu vết về mức tiêu dùng của mình. Khoảng trống giữa việc lưu trữ trạng thái nội bộ và các tác động phụ bên ngoài là một nguồn lỗi phổ biến trong các hệ thống xử lý hàng loạt và lập lịch tác vụ.
Tại sao điều này quan trọng
Đối với các đội ngũ vận hành phần mềm tự chủ (self-hosted) hoặc quản lý pipeline CI, việc hiểu rõ giới hạn của ghi nguyên tử là rất quan trọng đối với độ tin cậy và kiểm soát chi phí. Nhiều tác vụ tự động dựa vào logic thử lại để xử lý các lỗi tạm thời. Nếu các lần thử lại này không được đồng bộ hóa đúng cách, hệ thống có thể rơi vào vòng lặp vô tận hoặc làm cạn kiệt hạn ngạch API một cách không cần thiết. Điều này đặc biệt liên quan đến các công ty vừa và nhỏ sử dụng dịch vụ của bên thứ ba có trả phí, nơi mỗi lệnh gọi trùng lặp ảnh hưởng trực tiếp đến lợi nhuận. Đảm bảo rằng ngân sách thử lại được tuân thủ đòi hỏi nhiều hơn chỉ là I/O file an toàn; nó yêu cầu sự phối hợp cẩn thận giữa trạng thái và các tương tác bên ngoài.
Hơn nữa, khi ngày càng nhiều quy trình phát triển tích hợp các mô hình AI, độ phức tạp trong việc quản lý các tương tác này tăng lên. Các mô hình có thể chậm chạp, đắt đỏ và dễ bị giới hạn tốc độ (rate limiting). Một pipeline vô tình nhân đôi mức sử dụng API do xử lý đồng thời kém có thể nhanh chóng chạm ngưỡng giới hạn tốc độ hoặc gây ra các khoản phí bất ngờ. Các kỹ sư phải thiết kế hệ thống giả định rằng lỗi có thể xảy ra tại bất kỳ điểm nào trong quy trình. Bằng cách đặt trước tài nguyên trước khi sử dụng và sử dụng khóa để ngăn truy cập đồng thời, các đội ngũ có thể xây dựng các hệ thống tự động bền bỉ hơn, hoạt động dự đoán được ngay cả dưới áp lực hoặc trong điều kiện lỗi một phần.
Bạn có thể làm gì
- Kiểm toán logic thử lại của bạn để đảm bảo rằng số lần thử được tăng lên trước các lệnh gọi bên ngoài, chứ không phải sau khi hoàn thành thành công.
- Triển khai các cơ chế khóa hợp tác cho các tài nguyên chung để ngăn nhiều tiến trình hành động trên cùng một trạng thái cũ đồng thời.
- Sử dụng thời gian chờ ngắn cho việc lấy khóa để tránh deadlock, coi thời gian chờ hết hạn là một lỗi nghiêm trọng thay vì bỏ qua nhiệm vụ một cách im lặng.
- Thiết kế các cập nhật trạng thái để mang tính idempotent (lặp lại không thay đổi kết quả) khi có thể, cho phép các lần chạy sau đó đối chiếu sự khác biệt mà không lặp lại các thao tác tốn kém.
- Kiểm tra các kịch bản đồng thời một cách tường minh bằng cách mô phỏng sự cố tiến trình và các lần thực thi song song để xác minh rằng ngân sách và giới hạn được thực thi.
- Tách biệt việc đặt trước tài nguyên khỏi việc thực thi nhiệm vụ, đảm bảo rằng một nhiệm vụ thất bại vẫn tiêu thụ hạn ngạch đã được phân bổ để ngăn chặn các lần thử lại mất kiểm soát.



