かきスタンプ

福岡でフリーランスの物流系のエンジニアやってます。

Laravel : ジョブのタイムアウトを設定には、pcntl(PHPの拡張項目)を有効化する必要がある

Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7

 
ジョブを実行する時、以下のようにタイムアウトの時間(X 秒経過するとエラー)を設定できる。

php artisan queue:listen --timeout=60

他にも、タイムアウト時間を、コードに記述する事もできる。

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    /**
     * タイムアウトになる前にジョブを実行できる秒数
     *
     * @var int
     */
    public $timeout = 120;
}

優先順位としては、コードに記述した時間の方が高くなる。

例えば、「php artisan queue:listen --timeout=60」とコマンドを打っても、ソースコードでは「$timeout = 120」となっていた場合、タイムアウト時間は 120秒となる。

<Laravel 公式サイト>
https://readouble.com/laravel/8.x/ja/queues.html#cleaning-up-after-failed-jobs

が、実験したところ、ソースコードにて指定された「120」が有効とならなかった。

実はこの設定だけでは不十分で、「pcnt」という PHPの拡張項目を有効にする必要がある。
詳細は以下を参照。

# Timeout
https://laravel.com/docs/8.x/queues#timeout

The pcntl PHP extension must be installed in order to specify job timeouts.

pcntl を有効にするには、以下を参照してください。
PHP・Docker:Docker コンテナ起動の PHP にて、pcntl を有効にする方法

この設定を有効化すると、ジョブ実行のタイムアウト時間を設定する事ができます。

失敗したジョブを再実行

失敗したジョブを全て実行する場合、以下のコマンドを実行すると、失敗ジョブが再びキューに戻る。

php artisan queue:retry all

その後、「queue:work」や「queue:listen」等で、再び実行できる。

php artisan queue:listen

PHP・Docker:Docker コンテナ起動の PHP にて、pcntl を有効にする方法

<環境>
Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7

 
Laravel の特定の機能を使う場合、pcntl(プロセス制御機能)が要求される事があります。

それを使うためにはどうすれば? という事を調べると、「PHPコンパイルしてください」と凄いテンションが上がらない方法が見つかったりする。

PHP公式
PHP: インストール手順 - Manual

PHPがサポートするプロセス制御機能は、デフォルトでは有効となっていません。
プロセス制御機能を有効にするには、configure のオプションに > --enable-pcntl を付け、CGI 版あるいは CLI 版の PHPコンパイルする必要があります。

Stack Overflow
How to enable pcntl in php ( while using a framework like Symfony2 )
How to enable PCNTL on Ubuntu server 16.04 - Stack Overflow

普段、Docker を使って開発しているデベロッパーにとっては、なかなか辛みのある修正。

ですが、別に PHP をリコンパイルせずとも、Dockerfile を編集する事で pcntl(プロセス制御機能)を有効化する事は可能です。

具体的には、以下の記述を追加します。
Docker イメージは、公式配布の php-fpm を使う事を前提としています。

RUN docker-php-ext-configure pcntl --enable-pcntl \
  && docker-php-ext-install \
    pcntl

以下、Dockerfile の編集例。

FROM php:7.4.11-fpm

# install composer
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim

RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql

RUN docker-php-ext-configure pcntl --enable-pcntl \
  && docker-php-ext-install \
    pcntl

WORKDIR /var/www/html

Docker-composer を使っている場合、「docker-compose up -d --build」等のコマンドでリビルド。

pcntl が有効となっているかは、コンテナログイン後に以下のコマンドで確認します。

php -i | grep pcntl

以下の表示があれば、pcntl は有効となっています。

pcntl support => enabled

php コマンドのオプションについては、以下を参照。
PHP: Options - Manual

-m Show compiled in modules

参考サイト
https://github.com/codereviewvideos/docker-php-7/blob/master/Dockerfile

おまけ
一応、こういう方法もあるみたい。
Installing PCNTL module for PHP without recompiling

Laravel:Command クラスの handle メソッドに記述されている return 0 って何?

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

コマンドを作成する時、以下のようなコマンドで雛形を作ることが出来ます。 (例:SampleCommand クラス)

php artisan make:command SampleCommand

上記のコマンドで作成されるファイルは、こんな感じ。

app\Console\Commands\SampleCommand.php

class SampleCommand extends Command
{

// 中略

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return 0;
    }
}

handle メソッドの「return 0;」って何?
と思って調べてみたら、どうやら、コマンドを実行した時に帰って来る終了コードみたい。

終了コードって?

直前に実行したコマンド(ジョブ等)が、成功したのか失敗したのかを識別するためのコード。

Linux の場合、以下のコマンドで確認できる。

echo $?

0 : 成功
0以外 : 失敗(エラーコード)

といった感じ。

PHPユニットの実行結果の合否判定にも使えます。

(例)
php artisan test --testsuite=Unit

handle メソッドの return

コマンドから作成する雛形を実行した時、必ず 0 が返ってくるので、常に「成功」と判断される。

何かしらの処理を実行して、成功したか/失敗したかを判断したいなら、リターンコードを返してもいいかもしんない。

    public function handle()
    {
        $returnCode = $this->accountService->destroyLockedAccount();

        // 本当は直接返してもいいけど、説明するにはこっちが分かりやすそうだったんで。
        return $returnCode
    }

ちなみに return を省略した場合「0」が返る。

コマンドラインからコマンドを実行する場合「;」で繋げて実行結果を出力するといいかもしれません。

php artisan command:name ; echo $?

スケジューラでもリターンコードは取得できる?

調べてみたが、どうやら出来ないみたい。

$schedule->command の戻り値は event で、コマンドの実行結果を格納されるワケではない。
メソッドチェーンの先に実行結果のリターンコードを取得できるメソッドが無いか調べてみたが、どうやら無さそう。

という事で、スケジューラからコマンドを実行する場合、コマンドが成功したかどうかを判断する場合、各々のメソッドで識別が必要。

class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule)
    {
        $event = $schedule->command(SampleCommand::class)
                                ->everyMinute();

        dump($event->expression);  //=> "* * * * *"


        //// 本当はこんな感じで書いてみたかった
        //
        // $returnCode = $schedule->command(SampleCommand::class)
        // if($returnCode === 0){
        //     \Log::info('SampleCommand は正常に終了しました。');
        // }else{
        //     \Log::error("SampleCommand は異常終了しました。エラーコード{$SampleCommand}");
        // }

    }

Laravel:artisan route:list コマンド実行時の「_debugbar..」の情報をカットする

Laravel Debugbar は便利なんだけど、artisan route:list コマンドでルーティング情報をリストアップする時、先頭が「_debugbar」というノイズが入って来ます。

ですが、Laravel 8.34 以上なら、「--except-path」のオプションを付けると、上記のノイズを除去できます。

コマンド内容

php artisan route:list --except-path _debugbar

以下、Laravel 8.5.16 で実行しています。

--except-path オプションなし

f:id:kakisoft:20210507005726p:plain

--except-path オプションあり

f:id:kakisoft:20210507005749p:plain


参考
https://github.com/barryvdh/laravel-debugbar/issues/1046

Laravel:migration 実行時、MySQL の時だけ実行するコマンドを設定する

Laravel の migration は便利なのですが、テーブルのスキーマ定義が、Laravel の命令だけで解決できない事があります。

例えば、「テーブル名にコメントを付ける」といった命令は用意されていないため、実行したい場合は DBドライバごとの固有のコマンドを使用する必要があります。( Laravel 8 時点)

具体的には、DB::statement メソッドにて、ALTER TABLE コマンドを実行します。

しかし、ALTER TABLE コマンドは、ほとんどの場合、RDB固有のコマンドとなってしまうため、migration の本流に載せるのは避けたい。

そんな感じで、「データベースドライバが MySQL の時だけ実行したい」または「SQLite の時だけ実行したい」といった場合、getDriverName() にて、データベースドライバを名を取得して、識別する方法があります。

具体的には、以下のようなコード。

    public function up()
    {
        if (DB::getDriverName() !== 'mysql') {
            return;
        }

        DB::statement("ALTER TABLE `users` COMMENT 'ユーザマスタ'");
    }

上記の場合、MySQL の時のみ、ALTER TABLE コマンドを実行します。
SQLite の場合、DB::getDriverName() にて 'sqlite' という値を取得します。)

コードを書く場合、変に switch文で分岐させるよりも、個別のドライバ用に migration ファイルを用意して、該当のドライバでなければ早期リターンする、という構造がいいんじゃないかと思います。

Laravel : 【MySQL】int のサイズを指定しているにも関わらず、column_type が「int(11)」となってしまう

MySQL のバージョン : 5.7
Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7
 
Laravel で migration ファイルにてテーブルのカラムを追加する時、こんな感じで intのサイズを指定する事ができる。

    Schema::table('projects', function (Blueprint $table) {
        $table->integer('category_code')->length(3);
    });

が、実際に生成された MySQLスキーマを見ると、column_type は「int(11)」。
何故? 何か間違えた?

と思って調べてみたら、こんなのが見つかった。

https://stackoverflow.com/questions/25772759/schema-builder-length-of-an-integer

If you're using MySQL, you can't specify the length of an integer column. You can only choose between one of the available integer types, described at http://dev.mysql.com/doc/refman/5.1/en/integer-types.html

要は、「MySQL を使っている場合、長さの指定はできない」らしい。

どういう事かと言うと、MySQL の一部の型は、型によって既にサイズが確定しており、サイズを好き勝手に変える事は出来ないとの事。

その表が、こちら。
https://dev.mysql.com/doc/refman/8.0/en/integer-types.html

結論

MySQL で int を使うなら必ず『 int(11) 』になるけど、MySQL の仕様上、仕方ないから勘弁してね 」

じゃ、tinyint はどうなの?
という事で調べてみたら、こんなのがあった。

Laravelにてテーブルの既存カラムをtinyint型に変更できない問題

要は、
「int → tinyint に変更できない」
との事。(逆なら可)

逆が出来ないという事は、rollback が正常に動かないという事なので、採用は慎重に、というか見送った方がいいだろ。

実際、自分も余計なエラーに悩まされたし。

という訳で、int、bigint を使う場合は、デフォルトの長さでOK。
(というかそれ以外できない)

NVM for Windows を使ったけど Node.js のバージョンが切り替わらない! そんな時は、環境変数を見てみよう。

NVM for Windows を使うと、Windows 上で Node.js のバージョン管理ができるのですが、バージョンが切り替わらない現象に遭遇しました。
以下、その対策です。

NVM for Windows のインストール

Chocolatey(パッケージマネージャー)を使用しています。Chocolateyのインストール方法は、こちらでも。
ちなみに公式サイトはこちら

choco install nvm

別バージョンの Node.js をインストール

例:v13.7.0

nvm install v13.7.0

Node.js のバージョンを切り替え

例:v13.7.0

nvm use 13.7.0

これで Node.js のバージョンが切り替わってるはずが

λ node -v
v14.4.0

こんな感じで、マシンにインストールされている Node.js のバージョンが表示されました。

対策

環境変数が正常に設定されていない可能性があるので、設定し直します。

まず、コマンドプロンプトで「where nvm」と入力し、nvm のパスを調べます。
PowerShell には where コマンドは無いみたい)

C:\>where nvm
C:\ProgramData\nvm\nvm.exe

今回のケースの場合、「C:\ProgramData\nvm」を使用します。

システムの環境変数に、以下を追加します。

変数名 : NVM_HOME
変数値 : C:\ProgramData\nvm

続いて、コマンドプロンプトで「where node」と入力し、node.js のパスを調べます。

C:\>where node
C:\Program Files\nodejs\node.exe

今回のケースでは、「C:\Program Files\nodejs」を使用します。

システム環境変数に、以下を設定します。

変数名 : NVM_SYMLINK
変数値 : C:\Program Files\nodejs

こんな感じになります。
f:id:kakisoft:20200607113304j:plain

その後、環境変数の設定内容を反映させます。
(コンソールのウィンドウを開きなおす)

それでもバージョンが切り替わらない場合

公式サイトから配布されているインストーラを使用してください。
https://github.com/coreybutler/nvm-windows/releases/

nvm-setup.zip を選択し、「nvm-setup.exe」を起動してインストール。

f:id:kakisoft:20200607113316j:plain

こんなメッセージが出た場合は「はい」を選択します。
多分、この時に Node.js と上手い事やってくれてるんじゃないかと思います。