3 日坊主日記
2007-03-25 [長年日記]
_ [本] 市立図書館
DB勉強会参加したい! てことでRDBMSの基礎を学ぶ一冊。
比喩が楽しい。 こんなところに萌え要素。
闘うのはオプティマイザだが、武器はユーザが事前に提供しなくてはならない。……現実には、ほとんどのユーザは自分が武器を提供しなかったにも関わらず、戦いに敗れた者だけを責めている。オプティマイザが喋れるなら、さぞかし不満を述べるに違いない。(p.83)
オプティマイザ萌えー。
今、我々は教師としてSQLを通してオプティマイザに宿題を出すのである。……しかし現実はどうであろうか? 立場は教師であるが、それにふさわしい行動をとっている人はほとんどいないのではないだろうか。あまりに多くの宿題を出すと、学生が辛いのではと心配していないだろうか? データベースが可哀想に思えて、自分が直接宿題をしていないだろうか?(p.84)
_ [Rails] BackgrounDRb
BackgrounDRb いいね。win32 でも動くし。 単純な fork を実現する定石ぽい手順を紹介する。
時間のかかる処理を行うアクションがあるとする。 この処理をユーザが待つ必要はなく、バックグラウンド処理しても構わない類のものとする。
# app/controllers/board_controller.rb:
def post
@school = School.find(params[:id])
# ここで時間のかかる処理を行う...
redirect_to :action => 'index', :id => @school
end
時間のかかる処理を BackgrounDRb のワーカスレッドに置くことができる。
# app/controllers/board_controller.rb:
def post
@school = School.find(params[:id])
# worker を作ってすぐ戻る
MiddleMan.new_worker(:class => :post_worker, :args => { :id => @school.id })
redirect_to :action => 'index', :id => @school
end
# lib/workers/post_worker.rb:
class PostWorker < BackgrounDRb::Rails
def do_work(args)
@school = School.find(args[:id])
# ここで時間のかかる処理を行う...
ensure
kill()
end
end
処理コードを切り貼りするだけである。 ワーカスレッドでの ActiveRecord の呼び出しも問題ない。
対応する test も分離しよう。 functional test での MiddleMan は mock で置き換える。
# test/mocks/test/backgroundrb.rb:
class MiddleMan
def self.new_worker(opts={})
'dummy'
end
def self.get_worker(key)
'dummy'
end
def self.delete_worker(key)
true
end
end
これは Rails 流の mock 実現法であり、 test/mocks/ が vendor/ より先に探索されることを利用している。
# vendor/plugins/backgroundrb/init.rb: require 'backgroundrb'
test 環境では この require で test/mocks/test/backgroundrb.rb が読み込まれるわけだ。
do_work に移した処理に対応する test コードを worker test に移す。
# test/functional/board_controller_test.rb:
def test_post
room = rooms(:fuzoku_school)
post 'post', :id => room.id
assert_response :redirect
assert_redirected_to :action => 'index'
#assert_equal 1, ActionMailer::Base.deliveries.size
end
# test/unit/post_worker_test.rb:
class PostWorkerTest < Test::Unit::TestCase
fixtures :rooms
def setup
@middleman = BackgrounDRb::MiddleMan.instance
@middleman.set_sleep 0.05
@middleman.start_timer
@middleman.gc! Time.now
end
def test_post
room = rooms(:fuzoku_school)
job_key = @middleman.new_worker(:class => :post_worker, :args => { :id => room.id })
sleep 0.1 while @middleman.get_worker(job_key)
# 以降に do_work の test を記述
assert_equal 1, ActionMailer::Base.deliveries.size
end
end
ここでの setup は BackgrounDRb 自体の test を参考にした。 kill() されると @middleman.get_worker が nil になるので、それまで sleep して待つ。 test では do_work(args) を直接呼んでもいいかもしれない。
後半はrailsの話ばっかりでしたが>DB勉強会<br>来月もやるようなので次回はぜひ。