LoginSignup
15
9

More than 5 years have passed since last update.

和訳:sidekiq-shceduler README

Last updated at Posted at 2018-08-05

Ruby on Railsで定期的なバッチ処理を行うためのライブラリとして、sidekiq-schedulerというGemがあります。
これについて調べていたので、READMEの和訳を投稿します。

sidekiq-scheduler

sidekiq-schedulerはcronを模倣したSidekiqの定期的なジョブ生成の拡張機能です。

インストール

gem install sidekiq-scheduler

使用法

Hello World

hello-sheduler.rb
require 'sidekiq-scheduler'

class HelloWorld
  include Sidekiq::Worker

  def perform
    puts 'Hello world'
  end
end
config/sidekiq.yml
: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以外の個別ファイルにスケジュール設定を配置することもできます。

sidekiq_scheduler.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を使用します。

config/sidekiq.yml
: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で使用する場合、多くはrequireincludeを用いず、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のみから起動する場合、イニシャライザを下記のように記述します。

config/initializers/sidekiq_scheduler.rb
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.

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9