AR ホームベーカリー

オイラのアウトプット用ホームベーカリー!

RoR を Fargate で動かしていて Cloudwatch Event などに登録されていないのに cron 的な定期ジョブが動いていて何だこれは? と思ったら sidekiq-scheduler だった

タイトルがすべてです。

sidekiq-scheduler?

こちらです。

github.com

sidekiq-scheduler is an extension to Sidekiq that pushes jobs in a scheduled way, mimicking cron utility.

とのことで、 cron のような動作を Rails+Sidekiq の恩恵受けつつやる、と思ってもらえればオッケーです。

sidekiq-scheduler で動いているスケジュールの一覧を取得する

crontab で言う所の crontab -l ですね。

❯ bundle exec rails console

irb(main):001:0> Sidekiq.get_schedule
2024-04-15T10:56:35.242Z pid=134 tid=7ha INFO: Sidekiq 7.1.6 connecting to Redis with options {:size=>10, :pool_name=>"internal", :url=>"redis://ホスト名:6379"}
=>
{"スケジュール1"=>{"class"=>"Notifications::ExampleJob1", "cron"=>"0 0 1 * * *"},
 "スケジュール2"=>{"class"=>"Notifications::ExampleJob2", "cron"=>"0 0 2 * * *"},
 "スケジュール3"=>{"class"=>"Notifications::ExampleJob3", "cron"=>"0 0 3 * * *"},
 "スケジュール4"=>{"class"=>"Notifications::ExampleJob4", "every"=>"10m"}}

こんな感じで一覧が取れます。

スケジュールを追加する

この手でよく使われる、コードベースで crontab を制御する whenever と同じように、 /config/sidekiq.yml からコードベースで記述することができます。

---
:verbose: false
:concurrency: 5
:timeout: 25

:queues:
  - default

:scheduler:
  :dynamic: true
  :schedule:
    example_job_1:
      class: Notifications::ExampleJob1
      cron: '0 0 1 * * *'
    example_job_2:
      class: Notifications::ExampleJob2
      cron: '0 0 1 * * *'
    example_job_3:
      class: Notifications::ExampleJob3
      cron: '0 0 1 * * *'
    example_job_4:
      class: Notifications::ExampleJob4
      every: '10m'

動的 (プロセス稼働中) に追加する

:dynamic: true を付与していると、稼働中のプロセスに直接スケジュールを付与できます。 これは揮発性のはず (プロセスが死ぬと消えるはず)。

❯ bundle exec rails console

irb(main):001:0> Sidekiq.set_schedule('tmp-example_job_5', {at: '2024/04/15 20:05:00', class: 'Notifications::ExampleJob5'})
=> {:at=>"2024/04/15 20:05:00", :class=>"Notifications::ExampleJob5"}

irb(main):002:0> Sidekiq.get_schedule
2024-04-15T10:56:35.242Z pid=134 tid=7ha INFO: Sidekiq 7.1.6 connecting to Redis with options {:size=>10, :pool_name=>"internal", :url=>"redis://ホスト名:6379"}
=>
{"スケジュール1"=>{"class"=>"Notifications::ExampleJob1", "cron"=>"0 0 1 * * *"},
 "スケジュール2"=>{"class"=>"Notifications::ExampleJob2", "cron"=>"0 0 2 * * *"},
 "スケジュール3"=>{"class"=>"Notifications::ExampleJob3", "cron"=>"0 0 3 * * *"},
 "スケジュール4"=>{"class"=>"Notifications::ExampleJob4", "every"=>"10m"},
 "tmp-example_job_5"=>{"at"=>"2024/04/15 20:05:00", "class"=>"Notifications::ExampleJob5"}} # <- 追加された

注意すべきこと

和訳してくれている人がいるので、上からザッと読めばだいたいのトコは掴めます。

一番は、Rails の支援を受けているのでタイムゾーン指定もフレームワーク由来だと勘違いしそうになる点でしょうか。

cronの文法を使用する場合は、Railsのconfig.time_zoneでの指定ではなくサーバ側のタイムゾーンで解釈される点に注意してください。

qiita.com

とのことなので、掲題の Fargate 環境だと TZ=Asia/Tokyo のようにコンテナ内のタイムゾーンは補正してありますが、何も考えずに UTC になっている場合などは 9 時間の補正を忘れないようにしたいですね。