Ruby on Railsで定期的なバッチ処理を行うためのライブラリとして、sidekiq-schedulerというGemがあります。
これについて調べていたので、READMEの和訳を投稿します。
sidekiq-scheduler
sidekiq-scheduler
はcronを模倣したSidekiqの定期的なジョブ生成の拡張機能です。
インストール
gem install sidekiq-scheduler
使用法
Hello World
require 'sidekiq-scheduler'
class HelloWorld
include Sidekiq::Worker
def perform
puts 'Hello world'
end
end
:schedule:
hello_world:
cron: '0 * * * * *' # Runs once per minute
class: HelloWorld
sidekiqを実行します。
sidekiq -r ./hello-scheduler.rb
下記のように出力されるでしょう。
2016-12-10T11:53:08.561Z 6452 TID-ovouhwvm4 INFO: Loading Schedule
2016-12-10T11:53:08.561Z 6452 TID-ovouhwvm4 INFO: Scheduling HelloWorld {"cron"=>"0 * * * * *", "class"=>"HelloWorld"}
2016-12-10T11:53:08.562Z 6452 TID-ovouhwvm4 INFO: Schedules Loaded
2016-12-10T11:54:00.212Z 6452 TID-ovoulivew HelloWorld JID-b35f36a562733fcc5e58444d INFO: start
Hello world
2016-12-10T11:54:00.213Z 6452 TID-ovoulivew HelloWorld JID-b35f36a562733fcc5e58444d INFO: done: 0.001 sec
2016-12-10T11:55:00.287Z 6452 TID-ovoulist0 HelloWorld JID-b7e2b244c258f3cd153c2494 INFO: start
Hello world
2016-12-10T11:55:00.287Z 6452 TID-ovoulist0 HelloWorld JID-b7e2b244c258f3cd153c2494 INFO: done: 0.001 sec
オプション設定
設定オプションはsideliq.yml内に記述します。
下記が使用できます。
:dynamic: <有効にするとアプリ実行中にスケジュールを変更できます(デフォルト無効)>
:dynamic_every: <dynamicが有効の場合、ここで指定の間隔でスケジュールが更新されます(デフォルトは5s>
:enabled: <有効にすると本機能のスケジュールが動作可能となります[デフォルト有効]>
:scheduler:
:listened_queues_only: <キューがSidekiqにより監視されているジョブのみを生成します[デフォルト無効]>
スケジューラ設定
実行スケジュールの設定はSidekiq設定ファイルの:schedue
項目から行えます。
:schedule:
CancelAbandonedOrders:
cron: '0 */5 * * * *' # 0秒目から5分間隔で実行
queue_documents_for_indexing:
cron: '0 0 * * * *' # 1時間毎に実行
# デフォルトでは、ジョブ名はワーカークラス名からとられる。
# クラス名と異なるジョブ名をつけたい場合は```class```オプションを使用する。
class: QueueDocuments
queue: slow
args: ['*.pdf']
description: "This job queues pdf content for indexing in solr"
# ```perform```メソッドの最終引数としてスケジュールのメタデータを持つハッシュを渡すことを有効にする。デフォルトでは無効。
# as the last argument of the `perform` method. `false` by default.
include_metadata: true
# 全ジョブの有効/無効を設定。 デフォルトでは全ジョブが有効。
enabled: true
スケジュールメタデータ
performメソッドの引数からスケジューリング処理のメタデータをワーカーに渡すことができます。
これを有効にするには、各ワーカークラス項目に下記を追加します。
SampleWorker:
include_metadata: true
performeメソッドの引数を追加します。
def perform(args, ..., metadata)
# Do something with the metadata
end
このメタデータハッシュは下記のキーを持地ます。
metadata.keys =>
[
:scheduled_at # The epoch when the job was scheduled to run
]
スケジュール型
サポートするスケジュール型はcron, every, interval, at, in
です。
cron、every、intercal、は周期的にSidekiqにジョブを送ります。
cronでは秒単位でcronと同様のパターンで指定します。
:schedule:
HelloWorld:
cron: '0 * * * * *' # second = 0で実行
everyは指定の周期でトリガします。
every: '45m' # 45分間隔で実行
intervalはeveryと同様ですが、最後のジョブキューイングから指定の間隔が経過して次回の実行時刻をスケジューリングする点が異なります。
atとinは1回のみジョブ生成を行います。atは特定の時刻でスケジューリングします。
at: '3001/01/01'
DateTime.parseとChronicを理解すると、どんな文字列を用いても指定できるようになります。Chronicを使用する場合は依存モジュールとして追加が必要です。
inは指定期間の経過後にトリガされます。
in: 1h # 起動から1時間後にジョブを送信する
everyとcronでは配列を使用できます。
every: ['30s', first_in: '120s']
詳細はhttps://github.com/jmettraux/rufus-schedulerをご参照ください。
別ファイルからのスケジュールロード
config/sidekiq.yml以外の個別ファイルにスケジュール設定を配置することもできます。
clear_leaderboards_contributors:
cron: '0 30 6 * * 1'
class: ClearLeaderboards
queue: low
args: contributors
description: 'This job resets the weekly leaderboard for contributions'
個別ファイルはscheduleルートキーを持ってないことに注意してください。
この場合ではスケジュールをロードするには下記が必要です。
require 'sidekiq'
require 'sidekiq-scheduler'
Sidekiq.configure_server do |config|
config.on(:startup) do
Sidekiq.schedule = YAML.load_file(File.expand_path('../../sidekiq_scheduler.yml', __FILE__))
SidekiqScheduler::Scheduler.instance.reload_schedule!
end
end
上記のコードはアプリケーション起動時に実行されるイニシャライザ(config/initializers
)に配置します。
動的スケジューリング
スケジュールは起動後に変更できます。スケジュールを追加/更新するには下記が必要です。
Sidekiq.set_schedule('heartbeat', { 'every' => ['1m'], 'class' => 'HeartbeatWorker' })
対象スケジュールが不在の場合は生成、既に存在していれば更新が行われます。
:dynamicフラグが有効の場合、スケジュール変更は5秒間毎にロードされます。異なる間隔にするには:dynamic_everyを使用します。
:dynamic: true
もし:dynamic
フラグが無効なら、sidekiq側で明示的にスケジュールのリロードを記述する必要があります。
SidekiqScheduler::Scheduler.instance.reload_schedule!
Sidekiq.get_scheduleの呼び出しにより現在のスケジュールを取得できます。
タイムゾーン
cronの文法を使用する場合は、Railsのconfig.time_zone
での指定ではなくサーバ側のタイムゾーンで解釈される点に注意してください。
使用するタイムゾーンは明示的に指定できます。
cron: '0 30 6 * * 1 Europe/Stockholm'
また、Railsのconfig.time_zone
で許容される「Stockholm」のような略称が使えない点にも注意してください。タイムゾーン設定をconfig.time_zone
の値から行うコードを書く場合は、下記のように正しいフォーマットであることを保証してください。
ActiveSupport::TimeZone.find_tzinfo(Rails.configuration.time_zone).name
コネクションプールに関する注意点
通常時、cronとatのジョブはsidekiq-schedulerを実行するインスタンス数とは無関係に生成されますが、これはホスト間の時刻差が24時間未満であることを前提としています。
指定のジョブを複数回生成してしまうような異常条件は、下記があげられます。
- CPU高負荷+同時刻に100個など大量のジョブを生成。
- ネットワーク/redisレーテンシ+28(
MAX_WORK_THREADS
https://github.com/jmettraux/rufus-scheduler/blob/master/lib/rufus/scheduler.rb#L4を参照)以上のジョブがネットワークレーテンシウィンドウ以内の期間で発生。
every、interval、inのジョブはホストあたり1回のみ生成されます。
Sidekiq Webインテグレーション
sidekiq-schedulerはSidekiq WebインターフェースにRecurring Jobs
ページを追加する拡張機能を持ちます。
# config.ru
require 'sidekiq/web'
require 'sidekiq-scheduler/web'
run Sidekiq::Web
ActiveJobインテグレーション
sidekiq-schedulerをActiveJobで使用する場合、多くはrequire
やinclude
を用いず、ApplicationJob
を継承します。scheulerのロードとワーカーモジュールのインクルードはRailsが行います。
class HelloWorld < ApplicationJob
def perform
puts 'Hello world'
end
end
SpringプレローダとRailsコンソールでのイニシャライザのテスト
以前に示したイニシャライザでYMLファイルからスケジュールを取得する方法をとる場合、RailsとともにインクルードされるSpringプレローダがRailsコンソールでのテストと干渉する点に注意してください。
Springは変更がない限りイニシャライザをリロードしません。したがって、YMLスケジュールを変更して、それを確認するためにRailsコンソールをリロードしても、Springがそれを反映しないので変更前のように動作します。
変更後の動作を見るには、Railsコンソールの再起動の前にSpringの停止による再ロードを行ってください。
Unicorn/Railsサーバからのタスク管理
sidekiq-schedulerをsidekiqからではなくUnicorn/Railsのみから起動する場合、イニシャライザを下記のように記述します。
require 'sidekiq'
require 'sidekiq-scheduler'
puts "Sidekiq.server? is #{Sidekiq.server?.inspect}"
puts "defined?(Rails::Server) is #{defined?(Rails::Server).inspect}"
puts "defined?(Unicorn) is #{defined?(Unicorn).inspect}"
if Rails.env == 'production' && (defined?(Rails::Server) || defined?(Unicorn))
Sidekiq.configure_server do |config|
config.on(:startup) do
Sidekiq.schedule = YAML.load_file(File.expand_path('../../scheduler.yml', __FILE__))
SidekiqScheduler::Scheduler.instance.reload_schedule!
end
end
else
SidekiqScheduler::Scheduler.instance.enabled = false
puts "SidekiqScheduler::Scheduler.instance.enabled is #{SidekiqScheduler::Scheduler.instance.enabled.inspect}"
end
ライセンス
MIT License
コピーライト
Copyright 2013 - 2018 Moove-IT. Copyright 2012 Morton Jonuschat. Some parts copyright 2010 Ben VandenBos.