Passenger in development で Thread.local 使うとorz
current_user というメソッドがある。これは、現在接続中のユーザーオブジェクトを表す。
def current_user @current_user ||= WjUser.find(session[:wj_current_user_id]) rescue WjUser.anonymous end
で WjUser.anonymous は、匿名ユーザーを表す。頻繁に使うので、1スレッドごとにキャッシュするようにしている。
def self.anonymous(reload = false) if reload || Thread.current["wj_user.anonymous"].nil? Thread.current["wj_user.anonymous"] = self.find_by_login_name(PRIMITIVE_ANONYMOUS) end Thread.current["wj_user.anonymous"] end
で、こんなViewコードを書いてみた。
<%=h WjUser.id %><br/> <%=h WjUser.anonymous(false).class.id %><br/> <%=h current_user.class.id %><br/> <%=h WjUser.id %><br/> <%=h current_user.kind_of?(WjUser) %><br/> <%=h current_user.instance_of?(WjUser) %><br/>
結果。初回は1行目と1行目のidが一致するんだけれども2回目以降は、次のように
16579060 16154770 16154770 16579060 false false
リロードするたびに、WjUser.id は変化する。この理由は思うに、プロセスモデルのせいだと思われる。
Apache preforkだと、リクエストを処理しているスレッドは変わらないので(preforkだから1スレッドで動くはず、recycleされない限り同じ)、初回のリクエストのオブジェクトが保持される。development 環境で動かしているせいで、WjUserがRailsのdependenciesによって毎回定義されてしまう。したがって、初回のWjUserと二回目移行のWjUserが違うオブジェクトになってしまって、Thread.localに保存されたオブジェクトは初回のWjUserを指してしまう。
mongrel の場合は、毎回スレッドを生成して割り当てる(ただしRailsの場合、同時に動くのは1スレッド)ので、毎回WjUserが生成されると同時に、Thread.localも毎回異なるので、Thread.localに保存されたオブジェクトは同じWjUserを指す。
わかりにくいんだけれど。passenger の場合は、リクエストの生存期間 != スレッドの生存期間で、mongrelの場合はリクエストの生存期間 = スレッドの生存期間
となるとモデルオブジェクトを、リクエストの生存期間でキャッシュするにはcurrent_userと同じくControllerのインスタンスオブジェクト使うしかないですか。。。そりゃそうだ。Threadで誤魔化そうとしていた自分が悪い。