0

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 bus-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 bus-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

  1. Command bị kill giữa chừng (crash / Ctrl + C / container restart).
  2. Server hoặc container restart khi command đang chạy.
  3. Command chạy lâu hơn chu kỳ scheduler (ví dụ: > 1 phút với everyMinute()).
  4. Command bị timeout hoặc exception trước khi Laravel kịp release lock.
  5. 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 bus-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 bus-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.

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 bus-system ps aux | grep schedule

Hoặc:

docker exec -it bus-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 -it trong cron
  • Có logging cho scheduler để debug

Ví dụ cron:

* * * * * docker exec -w /var/www/html bus-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

Nguồn: atdev.blog - Nơi lập trình viên và người học công nghệ đọc bài hướng dẫn thực chiến, dùng công cụ dev ngay trên trình duyệt. Hoàn toàn miễn phí, không cần đăng ký.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí