LaravelのJob Queueの問題点と解決策
Laravelの非同期処理のために利用するJob queueにはいくつか課題が存在します。
本記事では、Jobの実行状況をObserveする方法について述べます。
問題点
ここではJobをQueue Databaseで管理することを想定します。(redis等でも同様ですが、SQSはちょっと分かりません)
Laravelではこの場合Jobがdispatchされると、jobs
テーブルに行が作成され、その中で実行状況(再実行回数やpending中かどうかなど)が管理されます。しかし、以下の問題があります。
-
JobをdispatchしてもJobのIDが返らないので、実行状況を知りたいJobをjobs tableの中から特定できない
- 単一のJobのdispatchでも、
Bus::batch
によるbatch dispatchでも同様です - そのため、Jobを作成したAPIリクエストのレスポンスから、どのようなJob行が作成され管理されているのかを知ることができません
- 単一のJobのdispatchでも、
私はこれを何気に不便に感じています。
例: Job Dispatch時の挙動
MyJob::dispatch($data);
// この時点でJob IDは取得できない
この課題を解決しようと、以下のようなPull Requestを作成してみましたが却下されました。
PRも雑だったか、そもそもJobのdispatch時にIDを返す意義に管理者は興味がないのかなと感じました。
解決策: JobQueuedイベントの利用
では他の方法がきっとあるのだろうと、調べてみたところ、JobQueued
イベントを活用すれば、Jobの実行状況をObserveする仕組みを構築できるなと気付きました。JobがQueueに登録されるとEventが発火され、そこにリスナーを登録すれば該当Job IDとJob Instanceに同時にアクセスできるのです。
実装手順
(GPT製。手元でも動きました)
リスナーの作成
php artisan make:listener JobQueuedListener
でリスナーを作成します。以下のように実装します。
namespace App\Listeners;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Support\Facades\Log;
class JobQueuedListener
{
public function handle(JobQueued $event)
{
$jobId = $event->id;
$jobInstance = $event->job;
Log::info('Job Queued', [
'job_id' => $jobId,
'job_class' => get_class($jobInstance),
'job_arg' => $jobInstance->arg, //Jobのpublic プロパティにアクセスできる
]);
//任意でデータベース更新
//Jobs tableに列追加して、そこで管理してもよし、別テーブル作ってもよしです。
}
}
リスナー上ではJobのpublic propertyとJobのテーブル上のIDを同時に受け取れるため、それらの紐付き情報をDBに保存する処理を書くことができます。
JobQueuedイベントリスナーの登録
JobQueued
イベントを受け取るリスナーを登録します。
プロバイダーに以下を登録します。
protected $listen = [
'Illuminate\Queue\Events\JobQueued' => [
'App\Listeners\JobQueuedListener',
],
];
まとめ
Laravelのデフォルト機能では、JobのIDを取得して即座に返す仕組みは提供されておらず、Jobの監視ロジックが組みづらいです。JobQueued
イベントを活用することで、Jobの状況を追跡する仕組みを構築できます。