GUIベースのタスクスケジューラー「laravel-totem」

# はじめに
Laravelでタスクスケジューラーが必要になった時、セオリーとしてはKernel.phpにscheduleメソッドをつらつらと書き、cronから毎分artisanコマンド `schedule:run` をコールするよう設定するはずだ。そして、それで十分というプロジェクトも多いだろう。
[Laravel 6.x タスクスケジュール](https://readouble.com/laravel/6.x/ja/scheduling.html)

しかしある程度タスクが増え、実行結果確認が毎度必要で、場合によっては再実行が求められるプロジェクトでは、このやり方では甚だ不十分である。ではどうするか？

その答えのひとつが、今回紹介する [codestudiohq/laravel-totem](https://github.com/codestudiohq/laravel-totem) である。

# laravel-totemが提供するもの
まず大前提として、laravel-totemはGUIで各種操作を提供している。GUIには、ブラウザから `/totem` というディレクトリに移動することでアクセスできる。

## 美しいGUI
百聞は一見にしかず。以下はタスク一覧。他の画面を見たい人は[コチラ](https://github.com/codestudiohq/laravel-totem#screenshots)。
![tasklist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142388/7178def8-75da-e9de-93b5-1482dce99a23.png)
## 認証
web上に公開されている画面となるため、認証は必須である。ただしlaravel-totemが提供するのは認証の一部分のみである。どういうことかは以下のコードを見てもらえばおわかりいただけるだろう。要は既存の認証機能を使い、結果だけをlaravel-totemに連携せよということである。

```AppServiceProvider.php
public function boot()
{
    Totem::auth(function($request) {
        // return true / false . For e.g.
        return Auth::check();
    });
}
```

あるいは、特定のIPアドレスからのみアクセスを許可するといったケースにも簡単に対応できる。

```AppServiceProvider.php
use Symfony\Component\HttpFoundation\IpUtils;

public function boot()
{
    Totem::auth(function($request) {
        // return true / false . For e.g.
        return IpUtils::checkIp($request->ip(), ['192.168.1.0/24', '192.168.1.15/32']) ? true : false;
    });
}
```
## 細やかなスケジュール設定
scheduleメソッドでは時間以外の細やかな設定ができるが、それとほぼ同等の設定がGUIでできる。時間はもとより、メンテナンスモード中も実行できるか、多重実行できるか、といったオプションを気軽に指定できるのがありがたい。

## 手動実行
画面上のアイコンから、手動実行ができる。例えばスケジュール通りに動くべきタスクがエラーでこけていた際の再実行などで役立つはずだ。ただし、後述の留意点にある通り`max_execution_time` に注意すること。

## 実行結果の記録
実行ログおよび、終了時間、処理秒数を記録する。これにより、例えば日々のパフォーマンスを確認してタスクのパフォーマンス改善の要不要を判断できるようになる。なお記録は希望日数分残すことができる。

## 実行通知
メール通知は言うに及ばずslack通知が簡単にできるのがありがたい。slackのwebhook URLを登録するだけでできる。ただ通知は非常に簡素なのので、若干物足りなさは感じるかもしれない。

# 導入の難易度
github上の説明を元に実施すれば、躓くことは無かった。ただし、`config/totem.php` を作りたい場合の手順は記載されていないので、以下のコマンドで実行する。

```bash

php artisan vendor:publish --tag=totem-config
```

# 留意点
実際使ってみたときに気づいた留意点は以下の通りである。

## 実行ログに記録される時間はDBのタイムゾーン依存
CURRENT_TIMESTAMPで日時を登録しているため、アプリはJST、DBのタイムゾーンがUTCというケースでは、実行ログの時間が実際の実行時間とズレているように見える。

## スケジュール実行はあくまでcron依存
laravel-totemでスケジュールを設定したとしても、cronから `schedule:run` コマンドを毎分実行しないと、スケジュール通りに動いてくれない。例えばdockerなどのローカル環境で開発しているときには、スケジュール通り動作するかの検証は難しいかもしれない。

## 手動実行はmax_execution_timeの制約下
cronからキックするのではなく、laravel-totemから手動でタスクをキックする場合、php.iniの`max_execution_time` の制約を受ける。つまりその値が30秒ということであれば、実行完了まで30秒を超えるタスクは、`Maximum execution time of 30 seconds exceeded` というメッセージとともにFatal Errorとなる。よってタスク実行専用のサーバーにてtotemを運用する場合は、思い切ってphp.iniの当該パラメータの値に余裕を持たせておくことが望ましい。一方通常のAppサーバーにてtotemを運用する場合は、各プログラム内で `set_time_limit(120);` というように都度制限時間を変更することが望ましい。

## テーブル名の衝突に注意
totemでは、スケジュール管理、結果保存のためにDBに専用テーブルをいくつか作成する。その中には `Tasks` などといういかにも衝突しそうな名前のテーブルが存在するため、`.env`に`TOTEM_TABLE_PREFIX`というパラメータを追加の上、`totem_`などのプレフィックスを設定しておくほうが良い。

## テーブル設定のキャッシュ問題
どういうタイミングで起きるかは不明であるが、すでにtotem用のテーブルを作成した後何らかの理由でこれらを削除して作り直そうとすると、以下のようなエラーが発生する。

```
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'homestead.fuga_task_frequencies' doesn't exist
```
これは、totemが下記のようにCacheにテーブル情報を格納した結果発生している。
https://github.com/codestudiohq/laravel-totem/blob/2d09bd8c9dd28aa04cfe037298323bf4718bb967/src/Totem.php#L94

したがってCashe先のデータを削除する必要がある。redisの場合は`keys *totem*`で関連するキャッシュが見つかるので、それらを指定して削除すれば良い。