よたらぼ
自分の興味の赴くままにIT技術系のネタを取りとめもなくメモっています。
Ruby言語やLinuxのネタが多いです。

November 27, 2002

[Ruby-GNOME2]バグレポート

が何件か来た。MLやSFのバグトラッキングシステムを使っている人はまだお行儀が良いのだが直メールをくれる人にちょっとカチンとくる内容のものが多い。

中には「0.1なんでどうせunstableなんだろうけどさー、リリース前にもうちょっと確認してよ(意訳)」なんてのもある。といっても今のところ、バグといえるバグはMLでレポートがあった1件のみなのだが(それ以外は直メール出してくる本人の勘違い)。

それにしても、覚悟はしていたものの、やっぱりGtk.initを使わずにエラーが出ると報告してくる人が多い。

ちなみに、Gtk.initはRuby-GNOMEでは無かったメソッドで、Ruby-GNOME2では一番最初に呼び出す必要がある。そうでないとGTK+がエラーを吐き、時にはセグメンテーションフォールトを起こす。

できれば、Gtk.initを呼び出さずにRuby-GNOME2のメソッドを呼び出したりしたら「Gtk.initしてくれ!」というエラーメッセージが出るのが一番良いのだろうけど、実はこれが難しい.....。どうしよっかなぁ。

ちなみに直メール、日本人が送ってくるというのは少ない(というか今回のリリース後は0)。その辺はわきまえている人が多いという事か(単に使ってる人が少ないだけだったりして(^^;))。

ドキュメントが少ないというのも問題の1つだろう。どうにかしないとなぁ。

[Ruby-GNOME2] Ruby-GNOME2メモはじめました

とにもかくにもドキュメントが必要だろうと言うことで。

とりあえずRuby-GNOMEメモをベースにRuby-GNOME2化していきます。

協力者ボシュー。

#ホントは英語版を先に用意した方が良いのかもしれないけど....。

本日のツッコミ(全4件) [ツッコミを入れる]

Before...

むとぽん [さっそく人柱になっていただきありがとうございます。 ここは直し忘れです。 Gtk::Window#window_p..]

たむら [情報どうもです。ちゃんと動作しました。set_titleとかも window_title= になるんですか ? とこ..]

むとぽん [set_titleはtitle=またはset_titleですね。 ちなみにうちのw3mはw3m/0.3.1ですが特に..]


November 27, 2003

[Misc] 某開発会社

サーバのスペックが低いんじゃボケーッ!ただでもっといいマシン持ってこーいっ!

とクライアント(アプリを開発してるのは別の会社)に言われたのですが、そんなわけないだろうと、こちらもその上に載っているアプリ(Weblogic + Oracle上で動くWWWアプリ)を調べさせてもらいました。

そしたら、なんと、1つのHTML(表)を生成するためにDBに(計算上)6,000 + 12,000 + 12,000回のアクセスしている画面がありました....。若干複雑な画面ではあるのですがなぜそこまで...。

WHERE句とかの存在を知らないんじゃないかとマジで疑ってしまいました。

よくよく聞くとそのうち12,000回のアクセスは不要だそうです...。え?

この画面ではないのですがソースも少し読みました。SQLは何かのツールで自動生成したらしくA4で1ページ以上あったりしてホントに必要な条件だけなの?と疑問に思ってしまいます。もちろん、IS NULLとかの遅くなりげな構文とかをふんだんに使っています。

ループで毎回DBに問い合わせをしているのはもちろんですが、StringBufferも知らないみたいです。惜しげもなくループの中でStringを使いまくりです。おかげで一時的とはいえ大量なメモリがおもしろいように消費されていきます。

PreparedStatementなんて論外です。そんなの彼らにはムズカシ過ぎです。

SQLの動的部分はもちろん文字列結合です。HTMLだけじゃないんです。文字列のエスケープだってループの中でやっちゃいます。6,000件の中から必要なキーを取得して次のSQLを生成しているのでそれもやむを得ません。

負荷試験と称し、20同時アクセスさせたらDBサーバが死にました。といってもクラスタが切り替わっただけなのですが、それもこれもDBのCPU使用率がしばらく100%に張り付いた状態になり、その結果、ヘルスチェック機能がレスポンス無しと判断しクラスタを切り替えたのです。DBサーバとしては正常動作です。でもクライアントはそれをDBサーバの所為にするんです。

#もちろんサーバのスペックをあげれば症状は減るかもしれませんが...。

そんなわけで、我々はこの開発会社の作るアプリをDBへのDDOSアプリと名付けることにしました。でも、フロントエンドのサーバもかなりの高負荷になるのでトロイの木馬とした方が良いかもしれません。実際トロイし<オヤヂギャグ。試験したのが深夜(サービスインしたサーバ上)なのでそんなオヤヂギャグでも大盛り上がりです。

とりあえず、Stringで結合しているものをStringBufferに置き換えただけで数分の一の処理時間になりました。実際に置き換えたモノで実演したところ、さすがのクライアントも納得していただけたようです。

たぶん、12,000件の余分なDBアクセスを除けばもっと早くなると思います。

あ、ちなみに私はエンジニアではないので、上記解析およびレポートは弊社のエンジニア数名に対応してもらいました。あるエンジニアはソースを見た瞬間に頭痛がしたそうです。温厚な彼が言うのですから、私が見たらキレてるに違いありません。

もちろん、メモリ・CPU使用率・ソースコードの解析結果・改善点の指摘等の詳細なレポート付きです .... ここまでやって無料。うがーっ!!!

そんなわけで感想

最近のMS Windowsサーバってある意味スゴイね。よく頑張った。UNIXだったらもう少しがんばったかもしれないけど、そこまでするアプリでもないしね。

それにしてもアプリベンダもハードのスペックやOSの所為にしないで少しは反省して欲しい。

StringBufferとかPreparedStatementの件はオレも昔やらかしたことがあるからあまり人のこと言えないんだけどさ。150秒かかって表示される画面なんて誰も使えんよ。

ついでに、ここまでしないと納得してくれないクライアントって...とっとと退散したい。

ちなみにここで書いた内容は若干フィクションが入ってます。と言っておきます。

次の日

別のアプリ(デーモン)でSocketExceptionが出ました。クライアントからアクセスされた後、reset by peerなんたらと出てその後の接続要求を受けつけなくなります。

アプリベンダ曰く、ソースに悪そうなところが見あたらないと言うことなのでネットワークのパケットをキャプチャして欲しい、というのです。

いろいろ言いたいことはあるのですがその部分のソースを見たわけではないのであまり疑うようなことも言えません。

そんなわけでさっそくパケットキャプチャを仕掛けました。今のところその症状は1回しか発生していないのでいつまでパケットフィルタを動かしっぱなしにするのかはわかりません。日々蓄積したデータはHDDを圧迫しないように手作業で破棄する予定です。

はー、オレは一体何をやってるんだ...。

本日のツッコミ(全2件) [ツッコミを入れる]

なひ [次の仕事のための勉強を有料でやらせてもらっている(無料なんでしたっけ(涙]

むとぽん [あれでは勉強になりませぬ...(T_T)。]


November 27, 2006

[Rails] Rails 1.2RC1対応

Rails 1.2RC1が出たのでRuby-GetText周り(CVS)の確認をしてみた。

大体は動いてるみたいなんだけど、テストが失敗するね。1.2リリースまでには直したいところだけど間に合うかな。すでにチャレンジされている方、パッチ歓迎です;)。

#DEPRECATIONのメッセージだけは直しました。

[Rails] ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy

Rails 1.2からは@cookies, @flash, @headers, @params, @request, @response, @sessionの各変数に直接アクセスするのがDeprecatedになっていて、そういったコードがあるとDEPRECATION WARNINGというメッセージを出すようになった。

それにしても、インスタンス変数にアクセスするだけでWarningを出す、なんて、どういう風に実装しているんだろう。インスタンス変数へのアクセス時にフックなんてできたっけな?と思ってちょっと調べてみたので、メモ。

本当の実装は、

activesupport-1.3.1.5618/lib/active_support/deprecation.rb

actionpack-1.12.5.5618/lib/action_controller/base.rb

activesupport-1.3.1.5618/lib/active_support/core_ext/module/attr_internal.rb

を見てほしいんだけど、それを超簡略化すると以下のような感じ。

# activesupport-1.3.1.5618/lib/active_support/deprecation.rbから抜粋
class DeprecatedInstanceVariableProxy
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
  
  def initialize(instance, method, var = "@#{method}")
    @instance, @method, @var = instance, method, var
  end
  
  private
  def method_missing(called, *args, &block)
    warn caller, called, args
    target.__send__(called, *args, &block)
  end
  
  def target
    @instance.__send__(@method)
  end
  
  def warn(callstack, called, args)
    puts ("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}")
  end
end
 
# 呼出側:actionpack-1.12.5.5618/lib/action_controller/base.rbから抜粋
class Test
  def initialize
    @_headers = []
    @headers = DeprecatedInstanceVariableProxy.new(self, "headers")
  end
  def headers
    #実際は、attr_internalでセットしている。以下を参照
    #activesupport-1.3.1.5618/lib/active_support/core_ext/module/attr_internal.rb
    @_headers
  end
end
  
# わかりやすいようにサブクラスで実験
class Test2 < Test
  def initialize
    super
    @headers[0] = 1  #ここ
  end
end
  
p Test2.new
実行結果:
@headers is deprecated! Call headers.[]= instead of @headers.[]=. Args: [0, 1]
@headers is deprecated! Call headers.inspect instead of @headers.inspect. Args: []
#<Test2:0x2aaaaab0a700 @headers=[1], @_headers=[1]>

ポイントは、DEPRECATEDなインスタンス変数にアクセスするというのはつまり、DEPRECATEDなインスタンス変数の*インスタンスメソッドに*アクセスする、ということ。

簡単に説明すると、まず、@headersの代わりに@_headersを用意。headersメソッドはこちらを使うようになる(attr_internalで設定)。
@headersは今までのインスタンス(ここでは配列)をそのまま使うのでは無く、DeprecatedInstanceVariableProxyを噛ませて、そのインスタンスメソッドにアクセスさせるようにする。
DeprecatedInstanceVariableProxyの中身はどうなっているかというと、インスタンスメソッドは全てundef。インスタンスメソッドにアクセス(ここでは@headers[0] = 1)されてきたらmethod_missing経由でWarningを出力し、元の(self.headers)メソッドを呼び出すようにする。
#なお、実行結果が2行出てしまっているのはご愛嬌(inspectのWarningのやつね)

なるほど、かしこいのう。というよりも、こういう(あると便利なんだけど、遅くなりそうで、かつ、あまりアプリ・ライブラリとしての本筋には関係のない)実装をスクリプト言語で躊躇無くしてしまうのがいろんな意味ですごい。
#もともとRailsは遅くなりそうな実装テンコモリだけどねー。


更新 設定