概要
Heroku で比較的短い時間で動作する定期処理を実装するのは簡単です。
しかし、長時間動作する定期処理を書く場合には注意が必要です。注意が必要となる Heroku の仕様に関する記述を以下に示します。
Dynos are also restarted at least once per day
1日1回は再起動するよ!
When the dyno manager restarts a dyno, the dyno manager will request that your processes shut down gracefully by sending them a SIGTERM signal.
再起動するとき SIGTERM を送るからよろしく!
If any processes remain after ten seconds, the dyno manager will terminate them forcefully with SIGKILL.
10 秒経っても生き残ってたら SIGKILL で息の根を止めるね♪
1日1回の再起動は避けることができず、時間が決まっているわけではなく予測が立たないためスケジュールで避けるといったことができません(たぶん)。
解決法
長時間動作する定期処理を書く場合は、以下の点に考慮する必要があります。
- 中断および再開を可能にする。
- 処理の進捗を保存しなければならない。
- 処理の進捗に応じて再開できるようにしなければならない。
- 任意のタイミングで 10 秒以内の途中中断を可能にする。
- 外部リソースへの出力に関する処理に特に注意が必要である。データベースやファイルサーバなど。
- 例えばデータベースアクセスではトランザクションを利用し、中断時ロールバックなどを行う。
- 複数外部リソースへ出力している場合は、一貫性を崩さない考慮が必要となる。
- 中断の可能性が常にあるため、進捗の保存単位が大きくなりすぎないようにしなければならない。
- 外部リソースへの出力に関する処理に特に注意が必要である。データベースやファイルサーバなど。
実装例
例えば以下のような形で対処することになるのではと考えています。
コンセプトを示すものであって、実際に動作するものではありません。
#coding: UTF-8 require 'clockwork' running = true trap('TERM') do finish_work end class WorkInterruptionException < StandardException; end module Clockwork handler do |job| progress = get_work_progress() while running begin case progress when 0 short_work when 1 long_work # ... 他にもたくさんの作業があるとする ... when 10 # 最後の作業だったとする last_work finish_wok end progress = progress + 1 save_work_progress(progress) commit_work rescue WorkInterruptionException => e rollback_work break end end end every(1.day, 'midnight.job', :at => '00:00') end def get_work_progress # 現在の作業の進捗をデータベースなどから取得して返す。 # 初回起動だった場合は 0 を返す。 # それ以外の場合は 0, 1, 2, 3 と作業が進むにつ入れて値が 1 ずつ増えていくものとする。 end def save_work_progress(progress) # 現在の作業の進捗をデータベースに保存する end def finish_work running = false end def rollback_work # 作業単位のトランザクションを全てロールバックする end def commit_work # 作業単位のトランザクションを全てコミットする end def short_work # 1つ目の作業を行う。10 秒以内に終わる程度の作業とする end def long_work # 2つ目の作業を行う。処理対象が多数あり、10 秒を超える可能性がある。 # 処理対象は targets 配列に格納されていると想定する。 # 全ての処理は1つのトランザクションで完結していなければならない。 targets.each do |target| raise WorkInterruptionException.new if !running # target に対する処理を行う end end
再開の考慮とかまだ足りてないですね…。
関連
コメント
本ページの内容に関して何かコメントがある方は、以下に記入してください。
コメントはありません。 コメント/heroku/clockwork/long_period