トップ «前の日記(2007-03-18) 最新 次の日記(2007-03-30)» 編集

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) を直接呼んでもいいかもしれない。

本日のツッコミ(全1件) [ツッコミを入れる]
# shachi (2007-03-25 07:58)

後半はrailsの話ばっかりでしたが>DB勉強会<br>来月もやるようなので次回はぜひ。

[]