Kiến thức chung, Kỹ thuật kiểm thử

Race conditions và lỗi liên quan

✅ Trong hơn 17 năm kiểm thử phần mềm, mình nhận ra rằng có một loại lỗi không dễ tái hiện, rất khó điều tra nguyên nhân, và đôi khi tạo ra nhiều tranh cãi giữa developer và tester. Đó là những lỗi liên quan đến “race conditions.

Race conditions là gì?

Hiểu đơn giản, race condition xảy ra khi nhiều tiến trình/ yêu cầu/ thao tác cùng truy cập và thay đổi một tài nguyên chung gần như cùng lúc, và kết quả cuối cùng phụ thuộc vào cái nào “về đích trước”.

Nói cách khác, hệ thống phần mềm đang cho phép nhiều hành động “chạy đua” với nhau. Nếu thứ tự hoặc trạng thái của những tiến trình/ thao tác này không được kiểm soát tốt, kết quả cuối cùng có thể khác nhau.

Một số đặc điểm đáng sợ của race condition là những lỗi này khó tái hiện vì không xảy ra thường xuyên – Chúng thường xảy ra khi nhiều user thao tác cùng lúc hoặc do điều kiện mạng nhanh/ chậm khác nhau.

Hai lỗi race condition thường gặp

Dưới đây là hai lỗi mình thường gặp trong các dự án khác nhau. Các lỗi này thường do thiếu cơ chế locking (khóa) trong cơ sở dữ liệu hoặc kiểm soát các transaction (giao dịch/ giao tác) chưa tốt.

Lỗi 1: Nhấn nút “Submit” 2 lần dẫn tới tạo dữ liệu trùng hoặc báo lỗi “đã tồn tại”

Ban đầu do vô tình, nhưng dần dần lỗi này luôn nằm trong danh sách “đoán lỗi” của mình khi kiểm thử các dụng web và trên mobile. Đó là bấm nút Submit 2 lần do bấm rồi mà không thấy “động tĩnh” gì nên bấm thêm lần nữa.

Trên thực tế, người dùng nhấn nút “submit” 2 lần do mạng chậm. Làm cho hệ thống nhận 2 yêu cầu (gần như) đồng thời. Kết quả là, hệ thống có thể tạo 2 đơn hàng trùng nhau, đặt 2 sản phẩm giống nhau, hay tạo 2 bình luận khác nhau trên facebook, v.v…

Lỗi 2: Thao tác trên một dữ liệu không tồn tại

Khi nhiều người dùng cùng thao tác trên một hệ thống. Rất dễ dẫn đến trường hợp hai người cùng sửa thông tin của một dòng dữ liệu. Có hai trường hợp xảy ra, có thể vẫn thao tác được nhưng kết quả là của hành động sau, hoặc báo lỗi “something went wrong.”

Mình cũng đã thử bấm nút Xác nhận 2 lần khi xóa 1 user, dẫn đến lỗi NullPointerException do user đó đã được xoá ngay ở lần bấm đầu tiên rồi 🤣

5 trường hợp liên quan đến race conditions nên thử

Trong mỗi dự án, mình thường chủ động thực hiện các test case sau và tỷ lệ phát hiện lỗi luôn rất cao:

1. Double click / Multiple submit

Nhấp đúp chuột vào các nút hoặc icon; hoặc thử bấm các nút chức năng – (CTA button như “Xác nhận”, “Delete”, “Book”, OK, v.v…) nhiều lần trên các màn hình/ hộp thoại.

  • Với một trang web chung, thử refresh/reload (tải lại trang) sau khi đã submit
  • Với UI, thử bấm nhanh nhiều lần vào 1 nút/icon chức năng nào đó (ví dụ Thanh toán, Đặt hàng, v.v…)
  • Với API, thử gửi cùng 1 request nhiều lần liên tục

2. Nhiều user thao tác cùng dữ liệu

Đối với các ứng dụng web thì trường hợp nhiều người dùng cùng thao tác trên một trường dữ liệu rất phổ biến, thường xuyên xảy ra. Ví dụ như 2 người dùng cùng sửa một bài viết/ record. Giám đốc và Trưởng phòng đang cùng duyệt một đơn xin nghỉ phép/ hay xin cấp ngân sách. Hay 2 người dùng cùng mua một ghế ngồi trên một chuyến bay.

Mời xem thêm edge case là gì?

Những trường hợp này thường phát hiện lỗi liên quan đến cách xử lý optimistic lock (khóa lạc quan – là phương pháp quản lý đồng thời trong cơ sở dữ liệu, cho phép nhiều giao dịch đọc/ghi cùng lúc mà không khóa bản ghi), xử lý tranh chấp, hoặc quản lý phiên bản của dữ liệu không hiệu quả.

3. Các giao dịch liên quan đến số lượng

Kịch bản nhiều người dùng cùng “tương tác” với một tài nguyên. Ví dụ, trong các sàn thương mại điện tử, một số chức năng dễ gặp race condition như quản lý tồn kho, áp dụng mã giảm giá (voucher, coupon), đặt chỗ, mua vé, hay săn sale giảm giá vào 1 khung giờ nhất định.

4. Gọi 1 API nhiều lần cùng lúc

Thường những trường hợp này, rất khó để thao tác thủ công, do khi dùng công cụ như Postman, thì nút Request sẽ bị disable sau lần bấm đầu tiên. Nếu dùng cURL trên terminal cũng phải gửi xong mới dán vào và bấm enter tiếp. Vì vậy, để thử những trường hợp này, bạn có thể cân nhắc sử dụng một số công cụ để hỗ trợ gửi nhiều yêu cầu đồng thời như JMeter hay K6.

Sau khi gửi 3 đồng thời yêu cầu “POST /create-order” cùng lúc, bạn cần kiểm tra xem dữ liệu có bị trùng không.

5. Refresh / Back / Retry request

Một trong những nguyên nhân khách hàng cứ báo lỗi mà chúng ta không tái hiện được là họ không đề cập bước “tải lại trang sau khi thực hiện 1 hành động nào đó.” Những lỗi này thường liên quan đến xử lý timeout và retry (thử lại).

Tuỳ cách lập trình, mà một số trang web, thao tác sẽ bị lỗi sau khi tải lại trang. Ví dụ người dùng tải lại trang sau khi đã nộp hồ sơ. Mạng chậm hoặc hệ thống của chúng ta đang bị quá tải nên xảy ra trường hợp timeout (quá thời gian chờ), người dùng thử tải lại trang >> dẫn đến xảy ra lỗi.


Cuối cùng, những lỗi liên quan đến race condition là lỗi/bug không dễ phát hiện bằng cách kiểm thử bình thường. Nó thường xuất hiện khi nhiều người dùng thao tác cùng lúc, nhiều yêu cầu giống nhau xảy ra song song, hay do hệ thống phần mềm xử lý không đồng bộ, v.v…

Vì vậy, tester chúng ta nên tập thói quen nghĩ về “các hành động diễn ra đồng thời” trong khi khi kiểm thử, đặc biệt với các hệ thống thương mại điện tử, đặt vé (xem film, khách sạn, tàu xe, máy bay), quản lý kho hàng/ cảng biển, duyệt hồ sơ như các hệ thống ERP, v.v…

Những bug race condition thường tốn thời gian để điều tra và khó xác định được nguyên nhân thực sự (defect – chỗ đoạn code gây ra lỗi), hoặc hay dễ bị bỏ qua (won’t fix) do “không tái hiện được” dẫn đến bỏ lọt bug này lên môi trường thực tế (production). Nhiều khách hàng phàn nàn nhưng chúng ta thì không sửa được do “không tái hiện được”. Đôi khi những lỗi này gây mất mát cho người dùng hay các công ty đối tác như: thanh toán 2 lần cho một đơn hàng, bán vượt kho, báo sao sai vì dữ liệu bị sai trạng thái.

Vì vậy các bạn tester đôi khi chỉ cần tự hỏi một câu đơn giản: “Điều gì xảy ra nếu 2 người dùng cùng thực hiện hành động này cùng lúc?”

Rất nhiều bug thú vị sẽ xuất hiện từ câu hỏi này.

Chúc các bạn kiểm thử hiệu quả!

You Might Also Like

Leave a Reply

Your email address will not be published. Required fields are marked *