かきノート

【Laravel】ジョブがタイムアウトした時に failed メソッドを実行するための条件

December 29, 2021 • ☕️ 4 min read

【 環境 】
Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7
MySQL のバージョン: 5.7

前回、こんなのを書きました。

【Laravel】ジョブがタイムアウトした場合、エラー扱いにして特定の処理を実行する事はできない?

<概要>
ジョブが失敗した時に自動で実行するメソッド( failed メソッド)が用意されていて、そこに失敗した時の処理を記述する事ができる。

しかし、タイムアウト時には failed メソッドが実行されない。

タイムアウト発生時に何かしらの処理を実行させる事はできないのでは?

色々試してみた結果、タイムアウト発生時に failed メソッドを実行する事が出来ました。

変更点1:pcntl を有効化する

実は、タイムアウト時間を自由に制御するには pcntl というプロセス制御機構が必要となり、これを有効化する必要がある。

詳細および設定方法については、こちらを。
Laravel : ジョブのタイムアウトを設定には、pcntl(PHPの拡張項目)を有効化する必要がある
PHP・Docker:Docker コンテナ起動の PHP にて、pcntl を有効にする方法

変更点2:待ち受けコマンドを「queue:work」にする

前回、キューの待ち受けコマンドに「queue:listen」を使っていたのですが、「queue:work」で動かしてみました。

具体的には、以下のコマンドを使用しています。

php artisan queue:work --tries=3 --timeout=10

ジョブのタイムアウト時間を 10秒に設定しています。

ソースコード

ジョブ実行時に、70秒のウェイトがかかるようにしています。

上記コマンドにて、タイムアウト時間が 10秒としているので、必ずタイムアウトが発生するようになります。

class MyJob12 implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 3;

    /**
     * Indicate if the job should be marked as failed on timeout.
     *
     * @var bool
     */
    public $failOnTimeout = true;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function handle()
    {
        \Log::info(__METHOD__);

        sleep(70);  // 70秒ウェイト

        \Log::info('70 second passed');
    }

    /**
     * ジョブの失敗を処理
     *
     * @param  \Throwable  $exception
     * @return void
     */
    public function failed(Throwable $exception)
    {
        // ユーザーへ失敗を通知するなど…
        echo "Send user notification of failure, etc...";
        \Log::info("Send user notification of failure, etc...");
    }
}

実行結果

[2021-12-30 03:24:41] local.INFO: App\Jobs\MyJob12::handle  
[2021-12-30 03:24:51] local.INFO: Send user notification of failure, etc...  

こんな感じで、タイムアウト時に failed メソッドが実行されました。

あとがき

「queue:listen」の場合、1度コマンドを実行すると後はずっと動いてくれるので、cron などで定期的にジョブ実行する仕組みが不要となる。
しかし、「queue:work」は1回しか実行してくれないので、本番環境やステージング環境で動かす場合、定時実行の仕組みを用意する必要がある。

ちょっと面倒だけど、タイムアウト時に特定のフラグを解除しないと後続バッチに影響が出る、といった場合はその方法がよさそう。

ちなみに、何故 listen と work の違いで、このような挙動の差が生まれているのかまではよく分かってません。
何となく、Laravel のバグっぽい気がします。


Relative Posts:

【MySQL・Laravel】insert 時にデッドロックが発生した時の回避策

February 15, 2022

【Laravel】DBファサードにて、where in 句を使用する方法

December 21, 2021

福岡の物流エンジニアが、七転び八起きしたあと九回転び、寝っ転がったまま何かやってる事を垂れ流しているブログ

RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon