- お知らせ -
  • 当wikiのプログラムコードの表示を直してみました(ついでに長い行があると全体が下にぶっ飛ぶのも修正)。不具合があればBBSまでご連絡下さい。

Ruby/Ruby on Rails/cron的なこと

はじめに Edit

Railsでcronを使わずにcron的な処理を走らせるのに便利そうなpoormans_cronプラグインに関して。

実装的には、ページにアクセスがあったときにフィルタで別スレッドを立てて擬似的にcron風処理をしているみたいです。
(逆に言うとtwitterボットみたいに完全放置系プログラムでページにアクセスが全然ない場合はcron処理は実行されません。そういう場合は、wheneverプラグインなどを併用して自身にcronでwegetするなどした方がよさげです。)

その他のcron的なことをする処理は こちら

※記事つくっておいてアレなのですが、(後述してますが)どうもpoormans_cron経由でモジュール関連のメソッドを呼び出すと、Railsのキャッシュのバグかもしれないような挙動に遭遇して筆者は使うのやめてます orz

特徴 Edit

ざっと見た特徴をば…。

構造(使い方):

  • PoormansCron::Cron.create(:name => 'hourly', :interval => 60 * 60) のようにhourlyという名前で1時間ごとにうごかすcronを登録
  • PoormansCron::Cron.register_job(:hourly) do end で上記のhourlyと名づけられたcronで実行する実際の処理をジョブに登録(複数登録可)
    例えば、1分毎のcronを登録して(これだけでは何もしない)、1分毎に実際に動かす処理をジョブに登録する形

長所:

  • 使い方が簡単なのでお手軽
  • 本物のcronに依存しないのでデプロイが楽
  • 実行される時はrails enviromentがすでに読まれているので、cron + script/runnerより起動が早いはず(passengerとかmongrel使ってるなら)

短所?:

  • webページにアクセスがないと登録タスクが実行されない
  • "何秒ごとに"という時間間隔でしか登録ができないので、cronの完全代用にはならないっぽい?(何日にとか何時にとか何曜日にとかのようなcronでできる指定は自分で処理を書かかないといけない)
  • 毎アクセスごとにフィルタで別スレッド立ててタスクをチェックしているので、すごくアクセスあるサイトの場合負荷がかかるような
    (うちはそんなにアクセスあるサイト作らないと思うので多分問題ないけど場合によっては)
    (それとベンチとかとってないで言ってます!!(無責任!!))
  • 1アクセスにつき1つのcronしか実行されないっぽいのでアクセスが少ないと(以下略
    (例えば10秒、60秒、1時間と3つのcronタスクがあった場合、アクセスごとに1つのcronづつ処理される。ただし、そのタスクのregister_jobで登録した複数のjobは1度に全部実行されるようです)

確認できた問題 Edit

  • 何故かin_progressが1(true)のまま、次のタスクが実行されないことがありました
    (ただし再現できてません orz なので公式にレポートは控えてる…)
    とりあえずの解決方法としてはwait_timeを小さく設定してタイムアウトを短くしておけばいいようです。 (下記のサンプルにもそれを追記する予定)
  • ジョブが途中で終了しちゃうヽ(`Д´)ノ ウワァァン!!
    どうも、ジョブが途中で終了するくさい。
    ActiveRecordとかでDB弄っていると途中で終了したり、
    トランザクション使うと必ずロールバックして更新できなかったり(涙 Thread.startしたのってjoinで終了待ちしないといけないんじゃないのかな?とか…。
    →どうやら、ジョブ中(シャブ中みたいな表現。。。)の自前のコードでモジュール使っているところで以下のエラーが起きて、トランザクション書いてたら全部ロールバックされちゃうという現象に遭遇?
    1回目は大丈夫で、2回目以降のジョブ実行でおきるみたい。
    A copy of XXX has been removed from the module tree but is still active!
    うーん。。。
    検索してみて、
    # config/environments/development.rb
    config.cache_classes = true
    と記述しておくと回避できるようですが、これだとファイル編集後に動的にロードされなくなるのでdevelopmentモードの意味なくなっちゃう><;;(webrickやmongerlのサーバーの再起動が必要になる)
    →該当モジュールのメソッドはscript/consoleからや、コントローラーから呼び出すと問題なく動く(´・ω・`)
    エラー詳細ページ作りました
    →結局解決できねえよヽ(`Д´)ノ ウワァァン!!→cron的な処理は別の方法をとることにしました orz

インスコ、初期設定等 Edit

いきなり注意点ですが公式のgithubのモノは常に更新されているようですので、READMEもよくよんでください。こちらに記載しているインストール方法や使い方が最新でない可能性もあります!!

インストールは

ruby script/plugin install git://github.com/jugyo/poormans_cron.git

でOK。

(readmeにもありますが)次にDBのmigrationファイルを作成します。

ruby script/generate migration CreatePoormansCrons

migrationファイルの中身は以下で、
※仕様が変わってmigrationする内容が変わっているかもしれないので、必ず公式のreadmeのところも参照してください

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CreatePoormansCrons < ActiveRecord::Migration
  def self.up
    create_table :poormans_crons, :force => true do |t|
      t.column :id,           :integer
      t.column :name,         :string
      t.column :in_progress,  :boolean, :default => false
      t.column :interval,     :integer
      t.column :performed_at, :datetime
      t.column :wait_time,    :integer
      t.column :async,        :boolean, :default => false
    end
  end

  def self.down
    drop_table :poormans_crons
  end
end

おもむろに、db:migrateします。

rake db:migrate

あとは、application_contoroller.rbにて include PoormansCron を追加する

1
2
3
4
# app/controllers/application_contoroller.rb
class ApplicationController < ActionController::Base
  include PoormansCron
  :

これでOKかな。

使い方例 Edit

前提:

  • 毎分とある処理(Hoge.do_moge)を動かしたい

まずは、cronに”毎分”処理をするようにcronを登録します。

『Rails で cron を使わずに cron っぽいことを行う方法』に解説があるように、DBのマイグレーションにPoormansCron::Cron.createでcronを登録してみます。

以下のようにしてmigrationファイルを作成します。

ruby script/generate migration register_minutely_to_poormans_cron

あとはできたファイルに以下のように記述。

1
2
3
4
5
6
7
8
9
10
class RegisterMinutelyToPoormansCron < ActiveRecord::Migration
  def self.up
    PoormansCron::Cron.create(:name => 'minutely', :interval => 60)
   end

  def self.down
    cron = PoormansCron::Cron.find_by_name("minutely")
    cron.destroy if cron
  end
end

PoormansCron::Cron.createの引数:

  • :name => 'minutely' は識別用のcronの名前
  • :interval => 60 は実行間隔(秒数)

おもむろにmigrate

rake db:migrate

あとは、以下を config/initializers/hoge_moge_for_cron.rb に作成して記述します。この例では、Hogeモデルのdo_mogeを実行してます。

1
2
3
4
# config/initializers/hoge_moge_for_cron.rb
PoormansCron::Cron.register_job(:minutely) do 
  Hoge.do_moge
end

引数:

  • :minutely 最初にcron登録時に指定した識別名。別に"minutely"(=String)でもいいみたい。

こんな感じで。あとは動作確認などをして完了。


Show recent 10 comments. Go to the comment page.

  • アップデートでpoormans_cronテーブルにasyncカラムが追加されたのと、インストールにapplication_controller.rbでの include PoormansCron の追加が必要になったぽいので修正しました。 -- TOBY 2010-03-09 (Tue) 20:31:16
  • とりあえず、エラーが起こる問題が解決できないので使用を断念( ´・ω・`)
    その他代わりに採用してみた方法については、cron的なことを御覧下さい。 -- TOBY 2010-03-15 (Mon) 17:14:48
Name:

Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New Pages Search Recent changes   Help   RSS of recent changes
Last-modified: 2010-03-11 Thu 15:52:32 JST (3561d)