定期実行処理を crono_trigger に移行したお話

こんにちは、エンジニアの ryopeko です。

今回は Data Platform と呼ばれているデータ集計基盤の Rails プロジェクトで定期実行用に使われていた gem、 sidekiq-scheduler を crono_trigger に移行したお話です。

なお Data Platform の記事については以前ブログで紹介したこちらの記事も合わせてご覧ください。

KaizenPlatform では非同期処理には長らく Sidekiq が使われており、Data Platform でも非同期処理が必要な部分で使われております。

Data Platform では集計処理を cron 形式で指定した日時に定期実行するという機能があり、そこでは sidekiq-scheduler が使われていました。

この sidekiq-scheduler は Redis に各種メタデータを入れておき、実行対象の日時にあるデータを見つけた場合に、登録された処理を実行するというものです。

しかし Data Platform の用途ではユーザーが実行したい処理と日時を登録するというものであり、sidekiq-scheduler の本来の用途とは違う使われ方をしていました。

sidekiq-scheduler は cron の置き換えとして使われるべきもので、実際スケジューリングも実行予定が書かれた静的な YAML ファイルを読みこんだり、initialize 時に実行内容を登録するという機能があります。

Data Platform ではユーザーの登録、更新により日時や実行の有無がダイナミックに変わっていくため、本来の用途がから離れるばかりか、Redis に Hash としてデータが入っているため一覧性に乏しいという運用上の問題も発生していました。

更に Redis は永続化を目的としたデータストアではないため、Redis 自体が落ちてしまった場合にリカバリー処理が必要になることがあります。実際運用の過程で Redis が落ち、登録されていた定期実行の設定も失われ、本来実行されるはずだった処理が実行されないという障害も起きていました。

Redis が落ちた時にデータをリカバリーするようにすればいいのですが、そもそもスケジューリング情報は永続化していて欲しいものなので DB にテーブルを用意してそちらに格納したほうがいいのでは?となりました。

管理運用するときに DB にデータが入っていたほうが他のデータと join 可能ですし、データがきちんとカラムに定義されていれば select をするだけで一覧性も Redis のときより格段にあがります。

ということで DB にスケジューリング情報を格納することにし、そのような gem がないか探して検討することにしました。

そこで以前から知っていた crono_trigger という gem の存在を思い出し、調査をした結果、我々の用途にハマるものということがわかりました。

この gem は DB に実行したいスケジュール(cron形式)、次回実行日時、エラー日時、エラー内容などを格納することができます。

f:id:kaizenplatform:20220228163558p:plain

この様に一覧性にかけては十分なものとなります。

設定や具体的な使い方は gem の GitHub リポジトリに書いてありますのでそちらを参照してください。

また今回この gem を選択する決め手のひとつに、worker 部分が ServerEngine を使っていることでした。

ServerEngine を使えばシグナルハンドリングを簡潔に的確に書くことができます。また、私も以前 ServerEngine を使った worker ライブラリを書いて運用していたこともあり、その安定性には懸念はありませんでした。

実際 worker 部分で問題が起きたことは一切なく、安定して動作しています。

更に crono_trigger 自体がマルチスレッドも考慮した作りになっているので、今後複雑な並列処理が必要になった場合も安心できると感じたことも決め手のひとつです。

移行には特に大きな問題はなかったのですが、唯一あったハマりどころとしては、worker を動作させる際に worker class を指定する必要があった点です。

worker class には以下のように module を include させる必要があるのですが、これとは別に worker が処理する class を起動時に設定する必要がありました。

class PipelineScheduler < ApplicationRecord
  include CronoTrigger::Schedulable
end
$ bundle exec crono_trigger PipelineScheduler

一応ドキュメントには言及は無いものの、コマンド実行例として引数を指定している箇所があるので、見落としていた自分が悪いというのもあります...

https://github.com/joker1007/crono_trigger#run-worker

今回は KaizenPlatform で使っているシステムで crono_trigger に移行したお話を紹介しました。

この gem は使い勝手の割に実はあまり知られていないのではないかと思っていたので、簡単な内容ではありますが日々のシステム運用にかなり役立っているということで紹介させていただきました。