Laravel 13.31은 중단 가능한 작업(interruptible jobs)을 추가하여, 큐 워커가 배포 중에 전송되는 SIGTERM 신호를 포착하고 작업을 중간에 중단하는 대신 깔끔하게 종료할 수 있도록 합니다. 수만 개의 행을 처리하거나, 대량의 이메일을 발송하거나, 보고서를 생성하는 등 장시간 실행되는 작업을 수행하는 팀은 새로운 릴리스가 배포될 때 데이터 일관성을 유지할 수 있어 큰 이점을 얻을 수 있습니다.

배포가 작업을 중단시켜 왔던 이유

새 버전이 배포될 때, Supervisor, Docker, Kubernetes와 같은 프로세스 관리자는 기존 워커에 종료를 위한 정중한 요청인 SIGTERM을 보내 중단을 지시합니다. 대부분의 워커는 종료하기 전에 현재 작업을 완료하기 위해 대기합니다. 문제는 관리자가 준수 시간을 단 몇 초만 허용할 때 발생합니다. 작업에 몇 분이 필요한 경우, 관리자는 SIGKILL로 격상하여 프로세스를 즉시 강제 종료합니다. 이로 인해 작업이 일관된 상태에 도달하지 못하고, 부분적으로 업데이트된 레코드, 고립된 파일(orphaned files) 또는 중복 작업이 남게 됩니다.

Laravel의 해답: 협력적 중단(cooperative interruption)

Laravel 13.31은 작업 클래스가 종료 요청을 인지할 수 있도록 구현할 수 있는 Interruptible이라는 이름의 컨트랙트(contract)를 도입했습니다. SIGTERM이 도착하면 Laravel은 작업의 interrupted() 메서드를 호출합니다. 프레임워크가 작업을 자동으로 중단하는 것이 아니며, 작업 스스로가 Laravel이 설정한 플래그를 확인하고 직접 종료해야 합니다. 이러한 협력적 모델을 통해 개발자는 현재 루프 반복을 마치거나, 부분적인 변경 사항을 롤백하거나, 종료 전 체크포인트를 기록할 수 있습니다.

작업을 중단 가능하게 만드는 방법

  1. 컨트랙트 구현 – 작업 클래스 정의에 implements Interruptible을 추가합니다.
  2. 작업 루프 내부에서 플래그 확인 – 매 반복마다 작업을 중단해야 하는지 확인하는 로직을 삽입합니다.
  3. 정리 작업 수행 – interrupted() 메서드 내에서 작업을 나중에 재개할 수 있도록 상태를 저장하거나, 사후 분석을 위해 중단 로그를 남깁니다.

가장 큰 함정: --once를 사용하지 마세요

php artisan queue:work --once 명령으로 큐 워커를 실행하면 신호 처리(signal handling) 기능이 완전히 비활성화됩니다. 워커는 단일 작업을 처리하고 종료되지만, SIGTERM 핸들러를 전혀 등록하지 않습니다. 결과적으로 모든 중단 가능한 작업이 종료 요청을 무시하게 되고, 프로세스 중간에 강제 종료됩니다. 이 패턴은 cron 기반 컨테이너나 원샷(one-shot) 배포에서 자주 나타납니다. 중단 가능한 작업이 필요한 경우에는 항상 데몬 모드(php artisan queue:work)로 실행하여 이 문제를 해결하십시오.

중단 감시하기

Laravel은 후킹할 수 있는 두 가지 이벤트를 발생시킵니다:

  • WorkerInterrupted – 워커가 SIGTERM을 받을 때마다 발생합니다. 이 이벤트를 로깅하면 배포가 워커를 얼마나 자주 중단시키는지에 대한 전반적인 현황을 파악할 수 있습니다.
  • JobInterrupted – 현재 작업이 Interruptible 컨트랙트를 구현한 경우에만 발생합니다. 이 이벤트를 사용하여 임시 파일을 삭제하거나 "마지막으로 처리된 행" 마커를 업데이트하는 등 작업별 정리 작업을 트리거할 수 있습니다.

이러한 이벤트를 모니터링하면 팀은 배포가 미치는 영향을 보여주는 대시보드를 구축하고, 빈번하게 중단되는 작업을 식별할 수 있습니다.

큐 크기 보고 기능 개선

이번 릴리스에서는 큐 매니저에 totalSize() 메서드가 추가되어 모든 큐에 대기 중인 작업의 총 개수를 반환합니다. 이 값은 실제 카운트를 보고하는 데이터베이스 기반 및 Redis 드라이버에서 신뢰할 수 있습니다. SQS, Sync, Beanstalkd와 같은 드라이버는 Laravel API를 통해 카운트를 노출하지 않으므로 여전히 0을 반환합니다. SQS를 사용하는 팀은 큐 깊이(queue depth) 확인을 위해 계속 CloudWatch 메트릭에 의존해야 합니다.

환경별 적용 방법

  • Docker/Kubernetes – 이제 우아한 종료(graceful shutdown) 기간을 효과적으로 사용할 수 있습니다. 작업이 플래그를 감지하고 깔끔하게 종료될 수 있도록 종료 유예 기간(termination grace period)을 충분히 늘려주세요.
  • Supervisor – stopwaitsecs를 중단 플래그를 받은 후 작업이 완료될 것으로 예상되는 시간보다 더 길게 설정하십시오.
  • 레거시 작업 – 관리자의 타임아웃보다 오래 실행되는 모든 작업을 검토하고, 가능한 경우 중단 가능하도록 수정하십시오.

반론: 추가되는 복잡성

이 기능이 적절한 타임아웃 설정을 대체하는 것은 아닙니다. 만약 작업 루프가 중단 플래그를 전혀 확인하지 않는다면, 관리자는 여전히 SIGKILL을 보낼 것입니다. 팀은 장시간 실행되는 코드 경로를 감사하고 논리적 중단점에 확인 로직을 삽입해야 합니다. 일부 개발자들은 이미 종료 유예 시간 내에 완료되는 짧은 작업에 대해 컨트랙트를 구현하고 플래그 확인 코드를 추가하는 등의 추가적인 보일러플레이트(boilerplate)가 번거롭다고 느낄 수 있습니다.

실행 체크리스트

  1. 배포 스크립트를 스캔하여 --once 플래그를 찾고, 중단 가능한 작업(interruptible jobs)이 사용되는 데몬 모드로 교체합니다.
  2. 가장 오래 실행되는 작업(수만 개의 행을 처리하거나 몇 분 동안 실행되는 작업)을 식별하고 Interruptible 컨트랙트를 추가합니다.
  3. 각 루프 반복 내부에 플래그 체크를 삽입하고, 필요한 마무리 코드는 interrupted() 메서드로 이동합니다.
  4. WorkerInterrupted 및 JobInterrupted에 리스너를 연결하여 배포가 프로세싱에 얼마나 자주 영향을 미치는지에 대한 메트릭을 수집합니다.
  5. Redis 또는 데이터베이스 큐의 경우 totalSize()를 사용하여 백로그를 모니터링하고, SQS의 경우 CloudWatch 알람을 그대로 유지합니다.

배포로 인한 종료를 갑작스러운 중단(abrupt kill)이 아닌 조율된 단계로 취급하십시오. Laravel 13.31은 데이터 일관성을 유지하고 작업이 중간에 중단되어 발생하는 운영상의 어려움을 완화합니다. 코드 책임이 약간 증가한다는 트레이드오프가 있지만, 대규모 백그라운드 프로세싱에 의존하는 팀은 더욱 신뢰할 수 있는 운영 환경을 확보할 수 있습니다.