
Laravel Scheduler Has Mutex khi dùng withoutOverlapping() – Nguyên nhân & cách xử lý
Laravel Scheduler báo “Has Mutex” khi dùng withoutOverlapping()? Đây là nguyên nhân lock bị giữ và cách xử lý đúng kỹ thuật cho production.
Laravel Scheduler bị Has Mutex khi dùng withoutOverlapping()
1. Context
Trong Laravel Scheduler (Kernel), ta có cấu hình:
$schedule->command('email:send')
->everyMinute()
->withoutOverlapping();
Khi kiểm tra lịch chạy trên server:
docker exec -it -w /var/www/html seasonbus-system php artisan schedule:list
Kết quả:
* * * * * php artisan email:send Has Mutex › Next Due: 12 seconds from now
Trong khi đó, nếu chạy thủ công:
docker exec -it -w /var/www/html seasonbus-system php artisan email:send
command vẫn hoạt động bình thường và gửi mail thành công.
2. Nguyên nhân
withoutOverlapping() trong Laravel Scheduler sử dụng cơ chế mutex (lock) để đảm bảo một command không bị chạy đồng thời nhiều lần.
Luồng hoạt động cơ bản:
11:10:00 → scheduler chạy email:send
Laravel tạo mutex lock: email:send
11:11:00 → scheduler chạy lại
Laravel kiểm tra thấy lock vẫn tồn tại
=> bỏ qua lần chạy mới
Điều này giúp tránh việc command bị chạy chồng (overlap).
Khi trạng thái hiển thị Has Mutex
Điều này có nghĩa là Laravel đang thấy lock vẫn còn tồn tại, nên scheduler không chạy lại command.
Các nguyên nhân phổ biến
Command bị kill giữa chừng (crash / Ctrl + C / container restart).
Server hoặc container restart khi command đang chạy.
Command chạy lâu hơn chu kỳ scheduler (ví dụ: > 1 phút với
everyMinute()).Command bị timeout hoặc exception trước khi Laravel kịp release lock.
Mutex vẫn còn tồn tại trong cache do lỗi runtime hoặc process không kết thúc đúng cách.
Lưu ý quan trọng
withoutOverlapping()có thời gian hết hạn mặc định là 1440 phút (24 giờ).Nếu command bị crash, lock có thể tồn tại trong thời gian dài cho đến khi:
hết hạn TTL, hoặc
được xóa thủ công
3. Cách xử lý tạm thời
Xóa mutex bị kẹt:
docker exec -it -w /var/www/html seasonbus-system php artisan schedule:clear-cache
Nếu thành công:
INFO Deleting mutex for ['/usr/local/bin/php' 'artisan' email:send].
Sau đó test lại scheduler:
docker exec -it -w /var/www/html seasonbus-system php artisan schedule:run -vvv
4. Cách xử lý lâu dài
4.1 Set timeout cho withoutOverlapping
$schedule->command('email:send')
->everyMinute()
->withoutOverlapping(5);
Ý nghĩa:
Nếu command bị kẹt, sau 5 phút lock sẽ tự hết hạn và scheduler có thể chạy lại.
4.2 Gợi ý cấu hình theo thực tế
->withoutOverlapping(5); // command chạy nhanh
->withoutOverlapping(10); // command trung bình
->withoutOverlapping(30); // command xử lý dữ liệu lớn
⚠️ Lưu ý:
Giá trị quá nhỏ có thể gây overlap thật nếu command chạy lâu hơn TTL.
Giá trị quá lớn có thể làm scheduler bị “đứng chờ” lâu khi có sự cố.
5. Lưu ý: schedule:list không chứng minh scheduler đang chạy
Lệnh:
php artisan schedule:list
chỉ hiển thị danh sách schedule đã khai báo trong code.
Nó không đảm bảo scheduler đang được hệ thống thực thi.
Cách kiểm tra scheduler thực tế
Trường hợp dùng cron:
* * * * * php artisan schedule:run
Scheduler chỉ chạy khi cron trigger mỗi phút.
Kiểm tra process (Docker):
docker exec -it seasonbus-system ps aux | grep schedule
Hoặc:
docker exec -it seasonbus-system ps aux | grep artisan
⚠️ Lưu ý:
Nếu dùng schedule:run qua cron, process thường chỉ tồn tại rất ngắn → có thể không thấy khi grep.
Khuyến nghị production
Dùng cron hoặc supervisor đảm bảo scheduler chạy mỗi phút
Không dùng
-ittrong cronCó logging cho scheduler để debug
Ví dụ cron:
* * * * * docker exec -w /var/www/html seasonbus-system php artisan schedule:run >> /var/log/scheduler.log 2>&1
6. Kết luận
Has Mutex không phải lỗi queue và cũng không phải lỗi Laravel Scheduler.
Đây là cơ chế bảo vệ của:
withoutOverlapping()
Mục đích:
→ tránh command chạy trùng trong cùng một thời điểm.
Khi xảy ra vấn đề:
Command bị crash
Container restart
Process bị kill
Timeout
→ mutex có thể bị giữ lại và khiến scheduler bỏ qua job.
Cách xử lý:
php artisan schedule:clear-cache
và cân nhắc cấu hình:
$schedule->command('email:send')
->everyMinute()
->withoutOverlapping(5);
Đồng thời đảm bảo hệ thống có process scheduler chạy ổn định mỗi phút.
👉 Nếu bạn chưa setup chuẩn production cho Laravel, bạn nên xem bài này để tránh lỗi scheduler + queue:
🔗 Laravel Server Setup: Cron + Supervisor + Queue

Trong bài này bạn sẽ hiểu rõ:
Cách đảm bảo
schedule:runluôn chạy đúng mỗi phútKhi nào cần dùng Supervisor để giữ queue worker luôn sống
Cách tránh job bị silent fail trong production
Best practice kiến trúc background job cho Laravel