herokuでresque-schedulerを動かす時に不定期の再起動と向き合う

resque-schedulerを使いたい時ってcronの代替のような、決まった時間にバッチ処理を実行したい、という用途ですよね。
resque-schedulerをherokuで使う時に注意しておきたいのが、heroku dynoでは不定期にdynoが再起動することがあり、
指定した時間にresque-schedulerがダウンしていることがある、ということです。

ダウンするといっても、デプロイによる再起動やdynoの1日1回発動する再起動とぶつかれば、です。
(ダウンしている(時間はアプリケーションslugにもよりますが)、おおよそ30秒程度です。dynoは自身が健全な状態に保つために1日1回必ず再起動します。)

1回くらい漏れても問題がないという特性をもつ処理の場合は大丈夫ですが、決まった頻度で必ず1回実行したい、という要件だとheroku上のresque-schedulerは厳しいです。
しかし、工夫でなんとかなります、という話です。

1) 冪等性のある実装にして、予め複数回エンキューする

要件との兼ね合いになりますが再送信時のリカバリ作業を軽減するために、機能として盛り込んでもいいかなと思っています。
scheduer.ymlの設定が冗長気味になるのであまり美しくない、、、。

2) SIGTERMを受け取ったらすぐにresque-schedulerがダウンしないように順番に再起動させる

herokuはdynoを再起動する時にコンテナ内のプロセスにTERMシグナルを送信します。このシグナルを送信してから30秒間プロセスが稼働し続けているとKILLシグナルを送信して強制停止してくる、という仕様があります。
このTERMとKILLの間の30秒で、resque-scheduler を順番に再起動することでダウンタイムをゼロにさせる、という作戦です。

dynoの再起動で、dynoへの停止処理dyno起動処理は微妙に平行に行われている(ログを見た感じ)ようなので、resque-schedulerはdynoが1つでもゼロダウンタイムはできそうな感じです。
確実に行うなら、resque-schedulerのdynoを2つにするとよさそうです。(要検証)

この実装は、gemを作っておりresque-scheduler のメソッドをオーバーライドすることでシグナルを受け取っても動くようになっています。(未完成)
https://github.com/jiikko/resque-scheduler-rolling_restart

3) Resque.enqueue_at でエンキューする

そもそもの問題は、ジョブをエンキューする時間にresque-schedulerがいないためエンキューされない、ということなので、
毎時5分に実行されるスケジュールをしている場合、2分前にResque.enqueue_at(3.min.from_now)を走らせておく、みたいなイメージです。(なおgemはなし)
Resque.enqueue_atを走らせておけば、発動時刻にresque-schedulerがダウンしていても起動した直後にエンキューされます。
エンキューされたジョブはRedisに問い合わせることで確認ができるので、理論上は重複してエンキューされることはありません。

4) ノーガード戦法

バッチ処理漏れを検出するツールを導入して、漏れが発生したら再実行する。シンプル。

以上。