2011年2月26日土曜日

Ruby on Rails と Thread

Rails2.2 の頃からスレッドセーフだとか何とか騒がれていましたがボクは知りません。知らないので、ネイティブスレッドではないRubyの Thread クラスを Rails で使っちゃおうという計画。delayed_jobではちょっと力不足だったのです。

やりたいことはただひとつ。
  • サーバで起こっている作業の途中経過をAjaxで取得したい。
コレ。簡単そうに思えましたが、ところがどっこい苦労しまくって体調崩すほどでした。誰か他にいい方法があったら教えてください。

つまりですよ。
  1. クライアントがファイルをアップロードする。
  2. サーバはレスポンスを返す。
  3. と同時に、サーバは新しく Thread.new して非同期に処理を開始し、処理をしながら進捗情報をどこかに保存しておく。
  4. クライアントは setInterval とかでサーバにリクエストを送信する。
  5. サーバは保存されている進捗情報を返す。
こんなことがしたかったんですね。

超問題になったのが「進捗情報の保存場所」。ぶっちゃけると session は使えませんでした。値が保持されるかと思ったら、スレッドの外と内でスコープが異なるようです。つまり 5 の時点で謎の値が返るわけです。

次に考えたのが app/models/ に新しくクラスを作成して、クラス変数に保持しておく、ということ。でもこれも駄目でした。development環境ではリクエストのたびにクラスがロードされるらしいので、予測不可能な段階でクラス変数が吹っ飛びます→NameError。production環境では動くかも知れないけど、さすがにそんな危ない橋は渡れません。

で、さんざん悩んだ結果、 lib/thread_manager.rb を作る、ということでした。

class ThreadManager
  @@progress
  def self.progress
    @@progress
  end
  def self.progress= value
    @@progress = value
  end
end


クラス変数に対するアクセサメソッドはマクロで定義できないようです。そんなに使う機会が無いとはいえ、あればもうちょっとスマートにできましたかね。

ともあれ、これで何とか凌げそうです。苦労はしましたが、得るものも大きかった…とは言い辛いぞ!正直Scala+Liftに乗り換えたいぞ!でもそれって、「隣の芝は…」ってやつですかね…。


0 件のコメント:

コメントを投稿