【Rails】wheneverを使ったバッチの定時処理化を理解する

# バッチ処理と定時処理
学びたての頃はバッチ処理と定時処理を混同している方も多いのではないでしょうか。
この2つは異なる意味を持ち、「バッチプログラムを定時処理している」というとわかりやすいと思います。

>バッチ処理...複数の処理（プログラム）を1つのまとまりとしたもの
>定時処理...決まった時間に行われる処理

例えば、顧客からの発注データを受け取った時に、データベースの更新など決まった処理を行うのも、定時ではありませんがバッチ処理です。


バッチ処理が定時に行われることが多い要因として、

- 人間の手が介入することなく自動でどんどん実行してくれる  
（エラーハンドリングで人間の判断を求めることはあります）
- 大量のデータを処理することが多いため、負荷が高くなることや、処理の途中でファイル操作されることを防ぐため、業務終了後に行う

といったものがあります。

本記事の目標は「バッチプログラムを定時処理にする」ことです。
そのため、実装するためには以下の2つのステップに分けて理解する必要があります。

1.バッチ処理を作る
2.スケジュールを設定する（定時処理にする）

では、順番に確認していきます。

# バッチ処理を作る
ここではバッチプログラムの作成と実行方法を理解します。

## プログラムの作成

＜作成手順＞
1.app/libの配下に「batch」フォルダを作成する
2.処理内容が伝わりやすいプログラム名でファイルを作成する（今回はdata_createとする）
3.処理を記述する

＜ポイント＞
最後に処理した内容や件数をメッセージとして表示させると実用性が高まります。

```ruby:app/lib/batch/data_create.rb
class Batch::DataCreate
  def self.data_create
    success = 0
    error = 0
    xxx.each do |xxx|
　　　：
      if xxx.save
        success += 1
      else
        error += 1
      end
    end
    p "#{success}件のデータを作成しました"
    p "エラーは#{error}件です"
  end
end
```

## バッチを実行する
1.config/application.rbに下記を追記し、バッチファイルを読み込むようにします。

```ruby:config/application.rb
:
class Application < Rails::Application
  config.paths.add 'lib', eager_load: true  # この行を追加
  :
end
:
```

バッチを実行してみます。

```:Terminal
bundle exec rails runner Batch::DataCreate.data_create
```

プログラムに記述したメッセージが出力されていれば成功です。

# スケジュールを設定する
ここでは耳馴染みのない言葉が多く登場します。
用語を確認したのち、設定を行っていきたいと思います。

## 用語を理解する

- cron  
設定されたスケジュールで定期的にプログラムを実行してくれるUNIX系OSの常駐プログラム。
- crontab  
スケジュールの追加、変更、削除を行うコマンド。  
このコマンドの実行によってcrontabファイルというものに、どのプログラムをいつ実行するかが記録される。このスケジュールをもとにcronがプログラムを実行する。  
- whenever  
crontabファイルの設定をrails上で記述できるようにするGem。

crontabコマンドだけで設定することもできますが、rails上で設定ファイルを管理できることからも、本記事ではwheneverでの設定をまとめていきます。

## wheneverで設定する
ここから実際にwheneverでの設定を行っていきますが、記述されている内容は「毎日2分毎にdata_createプログラムを実行する」ものです。
本記事の最後にスケジュールの設定方法をまとめておりますので、そちらを確認しながらご自身の環境に合わせて適宜変更を行い設定してください。


1.Gemファイルに下記を追加し、bundle installする
```gem 'whenever', require: false```

2.下記コマンドを実行し、スケジュールファイルを生成する

```:Terminal
bundle exec wheneverize
```

3.生成されたスケジュールファイル（config/schedule.rb）の最後に下記を追記

```ruby:config/schedule.rb
:
:
require File.expand_path(File.dirname(__FILE__) + "/environment")
rails_env = Rails.env.to_sym
set :environment, rails_env
set :output, 'log/cron.log'

#以下、適宜変更してください
every 2.minute do　　
  begin
    runner "Batch::DataCreate.data_create"
  rescue => e
    Rails.logger.error("aborted rails runner")
    raise e
  end
end
```

ターミナルにて```bundle exec whenever```を実行し、cron構文に誤りがないか確認する。（この時点では設定は適用されません）

3.設定を適用する

```:Terminal
bundle exec whenever --update-crontab
```
```[write] crontab file updated```と出力されれば適用されています。

補足：ターミナルにて```crontab -l```を実行すると、適用された設定を確認できます。

4.cronを使う
cronを起動することで設定したスケジュールでプログラムが実行されます。
以下、cronの使用に関するコマンドをまとめます。

```Terminal
#cronの起動
sudo systemctl start crond

#cronの停止
sudo systemctl stop crond

#cronの再起動
sudo systemctl restart crond

#cronの起動状態の確認（Active: active (running)」と表示されていれば起動中）
sudo systemctl status crond

#ログの確認（-fをつけるとリアルタイムで監視している状態になる。終わるときはCtrl + C）
sudo tail -f log/cron.log　　　 #ログファイルの場所はschedule.rbで指定している
```

# スケジュールの設定方法
wheneverを使うことでcronの構文を知らずともスケジュールは設定することができます。
しかし、schedule.rbで記述した内容に誤りがないか、実際にcronの記述で確認できるとより信頼性が増します。
ここではcronの記述を理解したのち、wheneverでの記述法を確認していきたいと思います。

## cron構文を理解する

まずはルールを確認します。

- cronの構文は「```分 時 日 月 曜日 [実行するコマンドやプログラム]```」の形で表現され、それぞれの指定の間は半角スペースで区切ります
- 時間の表記は午前午後の指定ではなく24時間表記で指定します
- 曜日は日曜日を0とし、土曜日の6まで対応しています（日曜日は7でも表現できます）
- 数字の代わりに*を入力すると指定なしと判断されます
- 各指定は「,」で区切ることでリスト形式で行うこともできます
- 各指定は「-」を使うことで範囲形式で行うこともできます
- リスト形式と範囲形式を同時に指定することもできます
- 各指定の後に「/」を使うことで一定の間隔を指定することもできます
- @rebootや@yearlyなどを使うことで、再起動時や毎年などの指定もできます

すべてのパターンをテストすることが難しいため、構文のみ記載していきます。
誤りがある場合はご指摘ください。

```
#毎月1日の23時59分に実行する
59 23 1 * * [コマンド]

#毎週月曜日の13時に実行する
00 13 * * 1 [コマンド]

#5分おきに実行する
*/5 * * * * [コマンド]

#毎日10時と18時に実行する
0 10,18 * * * [コマンド]

#毎日10時・11時・18時・19時・20時に実行する
0 10,11,18-20 * * * [コマンド]

#毎日10時から18時の間に1時間おきに実行する
0 10-18/1 * * * [コマンド]

#毎週月から金曜日の0時に実行する
0 0 * * 1-5 [コマンド]
```

## wheneverでの記述方法
※[引用](https://github.com/javan/whenever)

```ruby

#3時間毎
every 3.hours do 
  :
end

#毎日午前4時30分に実行する
every 1.day, at: '4:30 am' do
  :
end

#毎日午前4時30分と午後6時に実行する
every 1.day, at: ['4:30 am', '6:00 pm'] do
  :
end

#1時間おきに実行する
every :hour do 
  :
end

#毎週日曜日の午後12時に実行する
every :sunday, at: '12pm' do 
  :
end

#毎月27日から31日の0時0分に実行する
every '0 0 27-31 * *' do
  :
end

```

その他にも色々な記述方法がありますが、cronの構文を理解できているなら、最後の```every '* * * * *' do```の記述さえ覚えておけばある程度のスケジュールは設定できそうですね。

# まとめ
以上、バッチ処理の定時処理化について学びました！
今後絶対必要な知識になるので、もっと理解を深めていきたいと思います。