preload
12月 11

以下のように around_filter 内で reset_session するアクションに対し、大量のアクセスを重ねていくと、メモリ使用量ががつがつ増大していく。

class FugaController < ApplicationController
around_filter :foo
def index
end
protected
def foo
reset_session
yield
end
end

こいつを script/server -e production で起動し、ab -n3000 http://localhost:3000/fuga/ などにより多量のリクエストを送ると、メモリ使用量が 80MB を超えるまでに増大し、リクエスト完了後もまったく解放されない。foo 内で yield しなくても同様。セッションストレージにデフォルトの CookieStore を使った場合のほか、:active_record_store, :mem_cache_store を使っても同じ。session_options[:expires] の設定も関係無し。
なお、before_filter や after_filter で reset_session した場合は、問題は起こらないようだ。また、rails-2.0.2 環境で発見したが、rails-2.2.2 でも再現する。

状況解析のため、こちらのエントリ(Memory leak profiling with Rails) を参考にメモリ利用状況をプロファイルしてみたところ、around_filter を使った場合は String のインスタンス数が一気に膨れ上がった後、一向に解放されないことがわかった。(before_filter を使った場合は、リクエスト処理完了後、それなりに解放された。)

次に、この String の正体を見るために、上記の MemoryProfiler の :string_debug オプションを使って ObjectSpace 中の String をダンプしてみたところ、session_id などセッションデータと思しき文字列が大量に入っていた。リクエスト終了後は、CgiRequest や CGI::Session などのリクエストやセッションに絡むオブジェクトはすぐに解放されていた事から、何らかの原因でセッション内文字列のみ GC 対象にならない状態になっていると思われる。

さらに、上記の String がどの時点で生成されているのかを追ってみた。

actionpack-2.0.2/lib/action_controller/cgi_process.rb L.150-

private
# Delete an old session if it exists then create a new one.
def new_session
if @session_options == false
Hash.new
else
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
end
end

どうも上記の CgiRequest#new_session の CGI::Session.new で確保されているようだ。何が原因なのか追いたいが、CGI::Session は ActionPack で拡張されており、どうも切り分けが難しい。いろいろ書き換えながら試してみたところ、オリジナルの CGI::Session 中:

lib/ruby/1.8/cgi/session.rb L.299

ObjectSpace::define_finalizer(self, Session::callback(@dbprot))

ここのファイナライザ定義があることで、around_filter でのみリークするようだ(これを試しに無効にすると、リークしない。謎)。かといって、こんなところを無効にするわけにはいかず.. と、今度は around_filter と before_filter とで、何が変わってきているのか、を追ってみた。

actionpack-2.0.2/lib/action_controller/filters.rb L.709-

def run_before_filters(chain, index, nesting)
while chain[index]
filter, index = skip_excluded_filters(chain, index)
break unless filter # end of call chain reached
case filter.type
when :before
filter.run(self)  # invoke before filter
index = index.next
break if @before_filter_chain_aborted
when :around
yielded = false
filter.call(self) do
yielded = true
# all remaining before and around filters will be run in this call
index = call_filters(chain, index.next, nesting.next)
end
halt_filter_chain(filter, :did_not_yield) unless yielded
break
else
break  # no before or around filters left
end
end
index
end

ソースを追う限り、filter 処理は適用順序に並べた配列形式に変換された後、before_filter も around_filter も上記の ActionController::Filters::InstanceMethods#run_before_filters で実際のフィルタ処理がおこなわれているように見える。で、around_filter では、上記の filter.call(self) へ渡しているブロックで index = call_filters している箇所があるが、いろいろ切り分けていってみるに、どうもここが怪しい。around_filter に入れ子になっている filter を再帰的に処理しているようだが、ここの中で CGI::Session.new するとセッション中の String のみが解放されない状態になってしまう.. ということだろうか。

もうひといきな気がするんだが、ちょっとツラくなってきたので、とりあえずここまでで POST しておく。Ruby の GC の問題なのか、Rails の黒魔術のせいなのか、まだ微妙なところだ。とりあえず before_filter にしとけば問題は起こらないのだが、多数のフィルタ処理をサイトの全域で使うようなサービスの場合、個々のコントローラには around_filter をひとつ書いておいて、その filter 内に多数のフィルタメソッドを並べる、というのがラクチンだとおもう。うーむ、悔しいが before_filter で回避してしまおうか.. フィルタ内で、セッション検査をおこなった上で、不正セッションであれば reset_session して仕切り直す、みたいなのって、わりとあるような気がするんだけどなあ.. ハマってる人が見当たらない。

Continue reading »

Tagged with:
1月 21

ここのデザインを変更がてら、自サイト(tmty.jp) 配下のコンテンツを巡回していたら、trac がエラーを吐いていた:

Python Traceback

Traceback (most recent call last):
File “/home/****/local/lib/python2.4/site-packages/trac/web/main.py”, line 406, in dispatch_request
dispatcher.dispatch(req)
File “/home/****/local/lib/python2.4/site-packages/trac/web/main.py”, line 191, in dispatch
chosen_handler = self._pre_process_request(req, chosen_handler)
File “/home/****/local/lib/python2.4/site-packages/trac/web/main.py”, line 263, in _pre_process_request
chosen_handler = f.pre_process_request(req, chosen_handler)
File “/home/****/local/lib/python2.4/site-packages/trac/versioncontrol/api.py”, line 73, in pre_process_request
self.get_repository(req.authname).sync()
File “/home/****/local/lib/python2.4/site-packages/trac/versioncontrol/api.py”, line 91, in get_repository
raise TracError(‘Unsupported version control system “%s”. ‘
TracError: Unsupported version control system “svn”. Check that the Python bindings for “svn” are correctly installed.

これまで問題なかったのに、svn が Unsupported とか言い出してるのは妙だ。python が svn 連携に失敗しているのかな?
試しに、python のコマンドライン実行で確認してみる。

$ python -c “from svn import client”
:( 略)
… Shared object “libintl.so.6″ not found, required by “_client.so”

なんかいっぱいエラーが出たが、Shared object が無いって、なんで今更突然こんなエラーが出るようになっちゃったんだろ。最近サーバ設定いじった記憶ないし.. と、さくらのニュースアーカイブを見に行ってみたら、メンテナンス通知が出てた:

平素はさくらインターネットをご利用いただき、誠にありがとうございます。
さくらのレンタルサーバサービスにおきまして、サービスの安全性の向上を
目的とした、OS のバージョンアップを実施いたします。
ご迷惑をおかけしますが、ご協力お願いいたします。
:( 中略)
1/15(火) www731 〜 www740.sakura.ne.jp
1/16(水) www741 〜 www755.sakura.ne.jp
1/18(金) www756 〜 www770.sakura.ne.jp
:( 中略)
主な変更:

OS FreeBSD 4.10 → 6.1
:( 後略)

うお、もろ該当サーバに入ってた。これは影響でかいだろー。知らなかった.. サーバメンテの通知は、ちゃんとチェックしとかなきゃダメっすな。(でも、メールとかでリマインドしてくれても良いのになー。掲載から作業日程短すぎな気がする。)

4.10 => 6.1 での細かい変更点までは追ってないが、恐らくユーザインストールしたアプリ周りは、ほぼ reconfigure/rebuild が必要になるだろう。ということで、再設定開始。さくらのレンタルサーバでは、python は最初から入ってる(ユーザインストールしたものではない)ので、subversion 周りの reconfigure から開始。設定は覚えてなかったので、config.log で configure オプションを確認してから、SWIG-1.3.21 を $ ./configure && make && make install 、続いて、subversion-1.2.3 を $ ./configure && make && make install && make swig-py && make install-swg-py と。あと、念のため、svn-python/svn 以下の symlink を貼り直した。

とりあえず、こんだけで $ python -c “from svn import client” が通り、trac も動くようになった。

が、1/21 5:04 現在、機種情報検索サービス(rails環境)が復旧できてない。ruby/gem 周りも一通り再設定して、アプリ自体は script/console 環境下で問題なく動いているんだが、Web 経由で DB アクセスが発生するようなリクエストを投げると 500 になっちゃう。apache のエラーログって拾えないのかな?rails のログにはエラーが落ちてないんだが、DB アクセスしない範囲のリクエストは正常にさばけてるるっぽい。CGI 経由で DB に行ったときだけ利いてくる設定って.. どこだ?apache ログが拾えないと切り分けできないかな?うーん.. と、ここで力尽きたので、続きはまた今度。求む識者。

(2008/01/25 追記) その後.. とりあえず解決:

Continue reading »

Tagged with:
7月 09

Gmail でメーリングリストに subscripbe していると投稿した自分のメールが届いていないように見える(配信されない)が、これは仕様らしい。SPAM扱いになってるわけでもない。不便だ。自分宛にテスト投稿とかする場合も不便だと思う。

調べてみると、メーリングリストに投稿したメールや、自分宛に送信したメールは、POP受信できないようだ。ヘルプを読んでいる限りでは、「自分で送ったメールだったら、送信済みボックスに入っているだろう(それを見ろ)」というスタンスに見える。一方で、日々の戯言::weblob さんによれば、

一番簡単なのは、送信用メールサーバ(SMTPサーバ)をGmailのSMTPサーバとは別のモノにするってこと。分かりやすくいえば、Gmailのアカウントとは違うメールアドレスから送信するってこと。これなら間違いなく自分宛に届く。

つまり、自分宛のメールであっても、SMTP として smtp.gmail.com を使わなければ(別のSMTPで送信すれば)、ちゃんと受信できるということだ。実際に試してみたら、その通りだった。

で、不便なのは不便で仕方がない(いろいろ工夫してみるしかないぽい)んだが、なんでこういう仕様になってるのかを、ちょっとだけ考えてみた。

Gmailでは、同一メールと見なせるものについては、それらのメールをひとつのレコードとして扱おうとしているのかも知れない。こうすることで、多数のユーザにメールサービスを提供することで必要になる、多大なディスク(アクセス)容量やPOP転送トラフィックを削減しているのではないだろうか。

もちろん、これらは推測でしかないし、この仮説には、ちょっと考えただけでも、次のような疑問が浮かぶ。

  • どうやってメールの同一性を検出しているのか(Message-IDを使ってるのか、それとも他の何かか)
  • 重複分が、本当に無い(配信されるけど削除されている)のか、レコードとしては存在するけど表向きには非表示になっているのか

これらの疑問については、調べてみた感じでは、答えに辿り着けなかった。トラブルの解決に対しては殆んど意味の無い疑問なんだが、ちょっと気になっている。

不便な仕様ではあるが、google レベルのサービサーであれば、何か合理的な理由があるのだと思う(思いたい)。できれば、その理由を公開してほしいものだ。少なくとも、この仕様は、僕には考え付かなかった。将来、参考にできる思想があるのかもしれない。

Tagged with:
6月 12

NetBeans-6.0M9でRubyOnRailsするとgenerateでコケる

Ruby コメントは受け付けていません。

こないだGREEの勉強会で聞いたNetBeansでのRuby On Railsを早速試してみた。せっかくいじってみるので、最新の Preview Release (M9) を入れてみた。

例によって僕のPCはubuntu-7.04 feistyなので、デスクトップ効果を使っている場合は、Java6 u1を入れておいてやる必要がある。この辺はSunのKatakaiさんのブログが詳しい。

で、インストールが終わったら、インストール先ディレクトリの bin/netbeans を起動。おー、ちゃんと起動した。

[File]-[New Project] で Ruby On Rails Application が選択できる。Project Name を付けてやるだけで、cli 不要で、さくっと初期設定できちゃう。これはラクチンだ。IDE から Run すれば、デプロイ気にすること無く、WEBrick の起動からブラウザの localhost:3000 アクセスまで、全部やってくれる。

本家では 10分でできるブログシステム開発のチュートリアル も、flash ムービー付きで公開されている。ということで、早速試してみた。が、Controllerの [Generate] してみたら、右下にエラーアイコンぽいのが出た。NullPointerException 吐いたみたい。
エラーメッセージは

A java.lang.NullPointerException exception has occurred.
Please report this at http://www.netbeans.org/community/issues.html,
:

こんなかんじで、あと、ログには↓みたいのが出てた。

どうやらGUI周りでトラブってるぽいんだが、Classpath確認やらJDKの再設定やら試してみても、解決しない。

というところで、上記のKatakaiさんのエントリに、

Java6 u1 を使うと NetBeans 6.0 が GTK のネイティブルック&フィールになるのでうれしい

というのを発見。ん、Look & Feel が変わってるのか。何か関係してるかもしれない、と、試しに起動オプションで Look & Feel をいじってみる。

$ ./netbeans –laf javax.swing.plaf.metal.MetalLookAndFeel

これで、起動したGUIで [Generate] したら、ちゃんと generate 用のダイアログが表示された。GTK の Look & Feel 制御周りがまだ怪しいのかな。

ともかく、これでRails開発ごりごりできるようになった。デバッグ環境が最初から整ってるのが、なかなか良い。

Tagged with:
4月 30

Edgy -> Feisty のアップグレードは、メニューの [システム]->[システム管理]->[アップデート・マネージャ] であっさり行けた。dist-upgrade でも OK。結構入念にバックアップを取ってあったのだが、想像以上に簡単に終わってしまい、拍子抜け。OS のバージョンアップがこんだけ簡単だとウレシイなあ。いまのところ、日本語フォント名が日本語だ、くらいしか変更点に気づけてないけど、他にもいろいろあるみたい。パフォーマンス向上も結構あるらしい。

で、そのあと、Thunderbirdも2.0を使ってみようとしたが、こちらはうまくいかない。現在未解決。(2007/05/22追記、動いた。)

そもそも、本家で配布しているバイナリがSIGSEGV吐いちゃって、そのままじゃ動かない。

で、FTPサーバからソースを引っ張ってきて自前ビルドを試みた。

configure は、幾つかパッケージの不足があったものを追加インストールした後、id:unikerさん と同じ現象が出たので、同様に $ cp mail/config/mozconfig . で解決 (_ _)。でも、結局:

undefined reference to `nsHashKey::Write(nsIObjectOutputStream*) const’ …
:
/usr/bin/ld: final link failed: Nonrepresentable section on output
:
:

こんな感じでビルドできません。たぶん、launchpadフォーラムに上がってるこのレポートと同じ症状なんだけど、この、うまく行くか、まだ分からないfirefox用パッチを解読するのが吉なのか、おとなしくSynapticでThunderbird 2.0がサポートされるのを待つべきか。

そうそう、Feisty にしてから、beryl が、ちと不安定みたい。たまにタスクバーとか、ウィンドウが真っ白になってたりする。beryl 再起動すると復帰するんだけど、これもどこかに問題があるのかな。~/.xsession-errors 見ても、それっぽいエラーは吐いてないんだけど。。

その後、手動インストールに成功している勇者を発見。

1. Download Thunderbird 2. (Save to disk)
2. sudo tar -C /opt -zxvf ~/Desktop/thunderbird-*
3. sudo ln -s /opt/thunderbird/thunderbird /usr/local/bin/thunderbird
4. create a menu item:

なんと、実行場所がまずいだけだった。thunderbird起動スクリプト中の関連ファイルが解決できていなかったとか、その辺と思われる。もうちっとちゃんと調べたら分かってたかもなあ。

1.5からの移行については、2.0の起動前に ~/.mozilla-thunderbird を ~/.thunderbird にリネーム。失敗するとかなり寒いので最初はコピーにしときましょ。その後、起動すれば、1.5の設定、メール、spamフィルタの学習もちゃんと引き継げる。よかったよかった。

さっそくタグづけも使ってみた。物理的なフォルダは撤廃。MLやprojectに応じてフィルタ処理からタグを付ける。1-9のショートカットキーは3色ボールペンのノリで [1_important] [2_so_important] [3_interesting] だけ定義。to_reply, to_read も作ったけど、使うかは微妙。

デフォルトで右上の検索ボックスでタグ検索ができないのは痛いけど、GmailUI Extension を入れれば tag:<タグ名> で検索できる。よく使うケースは検索フォルダを定義すれば、いい感じにフラットな受信ボックスで使っていけそうだ。

うむうむ、いいかんじ。

Tagged with: