Skip to main content

2 posts tagged with "Concurrency"

Transactions, locking, lost updates and deadlocks — bugs that only appear with concurrent users.

View All Tags

Hai giao dịch cùng cộng 100, số dư chỉ tăng 100: isolation level qua thí nghiệm thật

· 13 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

Isolation level quyết định một giao dịch nhìn thấy gì khi có giao dịch khác chạy song song. Ở mức mặc định của hầu hết database là READ COMMITTED, hai phiên cùng đọc số dư 1000 rồi cùng ghi 1100 sẽ cho kết quả cuối là 1100 — một lần cộng biến mất, không có lỗi nào được ném. Nâng lên REPEATABLE READ thì phiên thứ hai nhận ERROR: could not serialize access due to concurrent update: câu trả lời sai âm thầm biến thành một lỗi rõ ràng mà ứng dụng phải thử lại. Toàn bộ số liệu dưới đây đo bằng hai session psql song song trên PostgreSQL 16.11.

Đây là loại lỗi gần như không thể tái hiện trên máy dev, vì ở đó bạn chỉ có một người dùng. Nó chỉ xuất hiện khi hai request thật chạm vào cùng một dòng trong cùng một khoảnh khắc — và lúc đó nó không sập, không log, chỉ làm sai số liệu.

Bài này đào sâu bài Transactions và ACID trong series học SQL 30 ngày.

When Does Calling .Result Deadlock, and When Does It Not?

· 13 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

.Result and .Wait() block the current thread. When await suspends, it captures the current SynchronizationContext so that the code after it runs back on that same context. If the context only allows one thread at a time — a WPF UI thread, an ASP.NET Framework request context — and that thread is the one .Result is blocking, the continuation can never get in: deadlock. Console apps and ASP.NET Core have no SynchronizationContext, so the continuation runs on the thread pool and nothing gets stuck. The real fix is async all the way.

The code below runs perfectly in a console app, and freezes solid the moment you paste it verbatim into an ASP.NET Framework controller or a button handler in WPF:

public string GetCustomerName(int id)
{
return GetCustomerAsync(id).Result; // hangs here
}

private async Task<string> GetCustomerAsync(int id)
{
var response = await _http.GetStringAsync($"/customers/{id}");
return Parse(response).Name;
}

No exception, no stack trace, no timeout. The thread simply stops forever. This is not HttpClient's fault, and async is not "broken" — it is the direct consequence of a design decision in how await works.