Ba bẫy hay gặp, đều dẫn tới việc code trông như có transaction nhưng thực tế mỗi câu lệnh tự commit riêng.
1. Autocommit — không mở transaction thì mỗi câu là một transaction. Gọi hai lệnh ghi liên tiếp ngoài khối transaction() thì lệnh đầu đã commit, lệnh sau lỗi không gỡ được lệnh đầu.
2. Dùng nhầm connection. Transaction gắn với một connection cụ thể. Nếu bên trong khối bạn vẫn gọi client toàn cục thay vì đối tượng tx được truyền vào, câu lệnh đó chạy trên connection khác của pool — nằm ngoài transaction và không bị rollback.
await db.transaction(async (tx) => {
await tx.insert(orders).values(order)
await db.insert(logs).values(entry) // wrong client: outside the transaction
})3. Lazy loading / flush trễ. ORM có thể hoãn việc gửi câu lệnh xuống database tới lúc flush, nên thứ tự thực thi thật khác thứ tự trong code — nhất là khi bạn xen lẫn truy vấn thô với API của ORM.
Cách kiểm chứng khi phỏng vấn hỏi "làm sao chắc chắn": bật log câu lệnh của database và tìm cặp BEGIN ... COMMIT bao quanh đúng những câu bạn muốn. Đây là cách duy nhất không phải phỏng đoán.