Rails 2.2.2のスレッドセーフ

Railsのスレッドまわりで苦労した身としては、Rails 2.2が謳っているスレッドセーフを検証してみないと…と思って、検証してみた。
rubygemsの1.3.1へのアップデートでいきなり躓いたけど*1、とりあえずアップデートは完了。BarControllerとZooControllerを作成、environment.rbに「config.threadsafe!」を追加して並列に動かしてみた。

BarController

class BarController < ApplicationController
  def index
    puts "#{Thread.current}: #{self} begin #{Time.now.strftime('%H:%M:%S')}"
    sleep 30
    puts "#{Thread.current}: #{self} end   #{Time.now.strftime('%H:%M:%S')}"
    render :text => 'bar'
  end
end

ZooController

class ZooController < ApplicationController
  def index
    puts "#{Thread.current}: #{self} begin #{Time.now.strftime('%H:%M:%S')}"
    10.times {|i| puts i }
    puts "#{Thread.current}: #{self} end   #{Time.now.strftime('%H:%M:%S')}"
    render :text => 'zoo'
  end
end

出力


#: # begin 23:32:42
#: # end 23:33:12
#: # begin 23:33:12
0
1
2
3
4
5
6
7
8
9
#: # end 23:33:12


…あれ?
スレッドセーフっててっきり「ロックの粒度を細かくして並列性を上げた」という意味だと思ってたんだけど…


で、しばし考える。
そういえばmongrel自体がリクエスト単位でロックしていたような。

mongrel-1.1.5-x86-mswin32-60/lib/mongrel/rails.rbの74〜78行目にリクエストの同期をとっている箇所があったので、試しにコメントアウトしてみる。
@active_request_pathがインスタンス変数なのが気になるけど、ファイル内では使われてないみたいだし、まあいいか…

rails.rb

#            @guard.synchronize {
              @active_request_path = request.params[Mongrel::Const::PATH_INFO] 
              Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
              @active_request_path = nil
#            }

出力


#: # begin 23:51:10
#: # begin 23:51:11
0
1
2
3
4
5
6
7
8
9
#: # end 23:51:11
#: # end 23:51:40

今度はちゃんと並列に動いた。

でも、こんなことして大丈夫かなぁ?
まさか「粒度の大きいロックでスレッドセーフになりました!」という訳じゃないだろうけど…うーん。

ActiveRecordの並列性も検証してみよう。

*1:1.2.0だとscriptが動かないです