Chống "Race Condition" trong Laravel
Có cái này nhỏ nhỏ mà cũng hay hay nên chia sẻ với anh em nè:
Chắc không ít anh em gặp vấn đề liên quan đến "race condition" (không cố tình thì là do click dup, cố tình thì là dùng tool), tức là trong một thời gian ngắn mà nhiều request giống hệt nhau dẫn đến nhiều tình trạng sai logic.
Để chống "race condition", có nhiều cách nhưng phổ biến là dùng Atomic lock (Laravel Docs). Dùng cách này thì không vấn đề gì, nhưng mình có viết một abstract middleware để anh em có thể dùng lại code một cách hiệu quả. Anh em tham khảo nhé:
1. Tạo Abstract Middleware
abstract class AbstractAvoidRaceConditionMiddleware
{
protected int $timeout = 10;
/**
* @throws RaceConditionException
*/
public function handle(Request $request, Closure $next)
{
try {
$keyLock = $this->keyLock($request);
} catch (Throwable) {
return $next($request);
}
$lock = Cache::lock($keyLock, $this->timeout);
if ($lock->get()) {
$exec = $next($request);
$lock->release();
return $exec;
}
throw new RaceConditionException($this->messageError());
}
abstract protected function keyLock(Request $request): string;
abstract protected function messageError(): string;
}
2. Sử dụng Middleware cho từng bài toán
Để dùng thằng này thì chỉ cần viết một middleware extend và khai báo keyLock + messageError như sau:
class AvoidRaceConditionWhenApplyMiddleware extends AbstractAvoidRaceConditionMiddleware
{
protected function keyLock(Request $request): string
{
$userId = Auth()->id;
$jobId = $request->integer('id');
if ($jobId <= 0) {
throw new \InvalidArgumentException('Invalid job id when apply job');
}
return 'apply_locking_' . $userId . '_' . $jobId;
}
protected function messageError(): string
{
return 'Apply quá nhiều lần cho cùng một tin tuyển dụng trong cùng một thời điểm';
}
}
3. Giải thích
- AbstractAvoidRaceConditionMiddleware giúp tái sử dụng logic tránh "race condition" bằng cách sử dụng Atomic Lock của Laravel.
- Các middleware con chỉ cần kế thừa và định nghĩa khóa lock riêng (keyLock()) cũng như thông báo lỗi (messageError()).
- Khi request tới, nếu khóa chưa tồn tại, nó sẽ thực hiện logic bình thường và giải phóng khóa sau khi hoàn tất.
- Nếu request bị trùng (chạy song song trong khoảng thời gian ngắn), nó sẽ báo lỗi để tránh thao tác lặp gây ảnh hưởng đến hệ thống.
🚀 Hy vọng sẽ giúp ích cho anh em khi xử lý các thao tác quan trọng cần tránh "race condition"!
All rights reserved