2010年3月14日日曜日

jRuby on RailsでPoolingRackApplicationFactoryのチューニング(jruby-rack)

前回、基本的なRailsスタック
(Apache + mod_proxy_balancer + Mongrel Cluster)と
Tomcat 6上で動かしたjRuby on Railsのベンチマークに関して投稿しました。
jRuby on Railsは、PoolingRackApplicationFactoryのセマフォ獲得処理に
多くの時間を費やしており、ランタイム数を増やしても、同時リクエストの
処理スピードが伸び悩むということが分かりました。

今回は、上記のJavaクラスをチューニングして、改めてベンチマークを
とってみました。

まず、チューニングの観点は以下のとおりです。

1.config.threadsafe!でRailsアプリケーションがマルチスレッド環境に
 対応している場合、プールしているRackアプリケーションに、
 前回の終了を待たず、逐次リクエストをディスパッチする。

2.セマフォやsynchronizedにおける同期処理を可能な限り削減する。

1.に関してですが、まずPoolingRackApplicationFactoryクラスの挙動を変える
仕組みを持たせるため、「warble.rb」に以下の一文を追記しました。

config.webxml.rails.threaded = true

config.threadsafe!メソッド自体は、その内部処理で個々のフレームワークに対し、
個別のフラグを立てているため、threadsafe!メソッド呼ばれたことを
きちんと判断するのが難しく(railsのバージョンの違いなどを考えるとなおさら)
また、jruby-rack内でRails.configurationに実行時にアクセスするのも、ちょっと・・・
というわけで、warble.rbに設定項目を追加し、コンテキストパラメータから
その設定が取り出せるように考えました。

上記のようなカスタマイズした変数は、warblerのgemのおかげで
何もせずともwarに同梱されるweb.xmlにしっかりと反映されます。
前半の「config.webxml」の部分は、web.xmlを出力する際の指示ですので、
結果的に以下のようなコンテキストパラメータが付与されます。


<context-param>
<param-name>rails.threaded</param-name>
<param-value>true</param-value>
</context-param>


次に、
2.に対するアプローチとして、まず以下のようなコンテナクラスを作成しました。

・内部的にArrayListに委譲し、add、getなどの必要な箇所のみロックをとる。
・要素はアクセスしてきたスレッドに対して均等に割り当てるようにする。
・あるスレッドに対する要素(RackApplication)が決定したら、
 スレッドローカルに要素のインデックスを格納して、次回はロックの獲得時間を
 最小限にする。

あえて、ソースコードは載せませんが、上記のrails.threadedがtrueの場合、
セマフォの初期化/獲得処理をバイパスし、既存のapplicationPoolに対する
synchronizedブロックも通らないように全て修正しました。
また、アプリケーションプールには作成したコンテクラスを
使用するように修正し、デプロイしました。

その修正版でNetBeansプロファイラを利用してプロファイリングを実施しました。



前回のようにgetApplicationはホットスポットではなくなっていますが、
railsアプリそのものの呼び出し処理がホットスポットとなっていますので、
これ以上のチューニングは困難そうです。

ここで、前回同様のjmeterのシナリオでベンチマークをとりました。

|チューニング前 | チューニング後
----------------------------------------
一覧表示|921 |766
作成 |2027 |1354
更新 |1972 |1380
削除 |2024 |1138
(単位はミリ秒)


驚くほどの性能向上は見られませんでしたが、一応
期待した改修は行えているようです。

尚、今回はランタイム数を3(min,maxともに)に設定しました。
これ以上ランタイム数を増やしても、
ハードウェア(といっても仮想マシンですが)の
限界で性能が頭打ちになるようでした。

しかし残念なことにランタイム数を1にして測定した場合と
今回の結果はほとんど変わりませんでした。

ランタイム数を増やしても前回のように何もしなければ、
セマフォの獲得処理に時間を消費し、今回のように改修を
行ってもランタイム数1の場合と、性能面でそれほど
変わりないのであれば、ランタイム数は1のままが良いと
いうことなのでしょう。

とはいえ、jRuby on railsが魅力的であることに変わりは
ありません。無形ソフトウェアでは
今後もjRuby on Railsおよび周辺技術を研究して行きたいと思っています。

2010年2月21日日曜日

railsとjruby on railsのベンチマーク比

今回は、最近話題のwarblerを使ってTomcatにデプロイした
rails(jruby on rails)と基本的なrailsスタックで、ベンチマークをとり、
パフォーマンスを比較、測定しました。

テストシナリオを以下のとおりです。

・サーバはscaffoldで作成したありがちな
  peopleコントローラとpersonモデルに対し、
  一覧表示、作成、更新、削除を行う(入力時間を考慮したタイマーは設けない)
・テストクライアントはjmeterを使用
・IDはCSVファイルからランダムに抽出
  (IDをパラメータから取得するようコントローラは修正済み)
・スレッド数50、ランプアップタイムは2秒
・合計30回以上のシナリオを走行して平均を取る

構成は以下のとおりです。

【共通】
・Windows XPをホストOSとするVMWare上で動作する仮想マシン
  (CentOS 5.4 X86_64)に
  メモリ1GBと2つのコアを割り当て
・MPMにworkerを使用したApache 2.2.3(スレッド数はテストクライアントを
  十分受け入れ可能だったのでデフォルト値)
・MySQL 5.0.77

【rails】
・ruby 1.8.7
・rails 2.3.5
・mongrel_cluster×4インスタンス
・productionモード

【jruby on rails】
・jruby 1.4.0(1.8.7 compatible)
・java 6(Sun SDK)
・Tomcat 6
・MySQLとの接続はJDBCコネクションプーリング
・productionモード
・Rubyランタイムは6個(config.webxml.jruby.(min|max).runtimes共に)
・ApacheとTomcat間はAJP1.3接続

それでは早速結果です。


|jruby on rails | rails
-------------------------------
一覧表示|921 |333
作成 |2027 |678
更新 |1972 |689
削除 |2024 |631
(単位はミリ秒)


平均レスポンス時間は2倍以上の差で
ノーマルなrailsスタックに分があることが分かりました。

当初、レースコンディションが発生するインスタンス変数は
全てスレッドローカルに保存するのだろうと、安易に考え
Javaサーブレット並にマルチスレッドで処理されることを
jruby on Railsには期待していたのですが、
見事に予想は裏切られました。

productionログに出力されるパフォーマンスは、Viewが十数ミリSec、
DBが数ミリSecであり、ノーマルなrailsスタックと比較しても、
高速に処理されているようですが、実はrailsレイヤーに到達する前に
ボトルネックがありました。

今回はNetBeansプロファイラを使用し、そのボトルネックの検証も
合わせて行いました。



org.jruby.rack.PoolingRackApplicationFactory#getApplicationが
ホットスポットであることが分かります。
さらにバックトレースから、
java.util.concurrent.Semaphore#tryAcquireで
セマフォを獲得している箇所に最も時間がかかっています。

jruby-rackのJavaソースを読む限り、warbler.rbで
config.webxml.jruby.max.runtimesを明示的に指定している場合、
セマフォ獲得が発生するようですが、リソース管理の観点から
ランタイムの最大数を設定することは避けられないように思います。

既存のJavaリソースを活用でき、商用も含め堅牢なJavaアプリケーションサーバで
Railsが動作することは、とても魅力的に思えます。
プレゼンテーションレイヤーが得意なRails、
サービスレイヤーを堅く作るのが得意なJava、
これらを見事に調和でき、さらに高パフォーマンスなアプリケーションが
構築できる有力候補としてjruby on railsにはとても期待しています。

次回は、jruby-rackのgemに含まれるJava部分を
独自にチューニングして、再度ベンチマークを
行ってみたいと考えています。

2009年11月9日月曜日

Rails2.3のActive Supportのnilがとっても便利な件

笑い暮らしドットコムでは
Ruby On Rails2.3を利用していますが、
今日はActive Supportが素のRubyをどれだけ便利にしてくれているか
という点に関して記述したいと思います。

タイトルの通り「nil」に関する話題です。

まず、もし手元にrubyがインストールされたマシンがあれば
irbを起動して試してみましょう。


irb(main):XXX:X> s = nil
=> nil
irb(main):XXX:X> s.class
=> NilClass
irb(main):XXX:X> s.nil?
=> true
irb(main):XXX:X> s.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
from (irb):50
from :0


変数「s」はnilで初期化しましたが、何らかの文字列を代入する予定の変数
だとします。やや作為的ですが、値には空文字も含まれるとしましょう。

もし、後続処理の条件判定で、文字列がnilと空文字以外であることを
判定したい場合、


unless s.nil? || s.empty?


と書かなければなりません。

長い上に、unlessと||演算子の組み合わせを読んだときに一瞬
ドキッとします。

ここで、Ruby環境のnilについて整理しましょう。

nilはNilClassのシングルトンのインスタンスです。
よって、nil.nil?などのメソッドを利用しても
JavaのようにNullPointerExceptionと怒られることは
ありません。(このあたりはJavaの経験年数が多い私にとっては
ある意味カルチャーショックです・・・)
また、nilで初期化された変数は、なんらかの
インスタンスが代入されるまでは、
ずっとNilClassのインスタンスのままです。

よって上記の判定で、


unless s.nil? && s.empty?


のように、誤った論理演算子を使用してしまったり、後続の処理で
s.empty?をコールする前に、Stringのインスタンスが未代入のままで
あった場合、NoMethodErrorが発生してしまいます。

いずれのケースも単純なバグですが、こんなところで
つまづきたくありません。

RailsのActive Supportは素晴らしいアプローチでこれを解決して
くれています。

それでは、Railsの環境がある方は、早速script/consoleで試して
みましょう。


s = nil
=> nil
>> s.nil?
s.nil?
=> true
>> s.blank?
s.blank?
=> true


nilにblank?というメソッドが存在するのが分かります。
Stringはどうでしょうか?


>> s = ""
s = ""
=> ""
>> s.blank?
s.blank?
=> true


Stringにもblank?メソッドが存在していますね。

すると、nilおよび空文字以外の判定は、どうなるのでしょうか?


unless s.blank?

だけです。

ステキですね。nilもStringインスタンスの空文字も
存在しないことが同じメソッドで判定できますし、
単純な英語を読んでいるような感覚でソースコードを
読むことができます。

Oracleでは、挿入の際に文字列が空文字であれば
nullがそのカラムに入りますが、MySQLでは空文字は空文字のままです。
Active Recordでデータベースのレコードを読み出す際に、
エンティティのフィールドはそのカラムの値に依存するので、
nullであればフィールドはnil、空文字であれば空文字が
格納されます。

その際、blank?一発で空文字判定ができるのは、相当な
メリットだと感じています。
こういう細かいところに手が届くのはさすがですね。

2009年11月3日火曜日

Ruby On Rails 2.3でリッチな大喜利2.0

無形ソフトLLCは、この度自社サービスとして
「笑い暮らしドットコム」をリリースしました。

暇なとき、疲れたとき、思わずクスッと笑ってしまうような
場所をご提供できたらと思っています。
また、笑いのセンスに自信のある我こそはという方は
このサイトを使って、どんどん笑わせてくださいね。
サイトは大喜利だけに純和風な感じに仕上げています。


トップページ



サイトのベースはお題に対する回答を募集し、それを見ることが
できるという風に、まさに「大喜利」形式なのですが、
回答の見せ方にも少しこだわりました。


お題の一覧



最初から表形式で回答が見えてしまっていたり、
リンクをクリックした時にすぐに回答が見えてしまったのでは、
面白さが半減してしまうかもしれないと思ったのです。
例えば、テレビでフリップを出すとき、面白い芸人さんは
絶妙な間がありますよね。
その間をWebで表現できればと思っているんです。
さらには、手書きのフリップにはない効果を突き詰めていけば
「大喜利2.0」と呼べる新しい何かができるのではないかと
考えています。とはいえ、現在も回答の見せ方は研究中ですが。。。


回答のページ。Atomフィードで現在のランキングを随時配信中。



また、イメージやアニメーション、音楽などをお題にした
大喜利もやっていこうと考えています。
これもWebならではという所に着想を得ています。
現在準備中ですが、完成をお待ちいただければと思います。

現在サービスを開始している「テキストで笑わす」は
お題に対する回答を見るページで、以下のフォームから
回答を送信することができます。

回答フォーム



回答は長くならないように、お題を工夫するつもりです。
また、このブログにも貼ってある笑い暮らしドットコムの
ウィジェットからもJSOPで回答を送信できるようにしました。

ウィジェットを張ってあるブログなどのページを表示した際に
お題がランダムに切り替わるつくりになっています。
また、そのお題で「ウケた」という投票が最も多い回答が、
マウスカーソルを合わせると表示されます。

ブログに一ネタ欲しい方は是非ご利用ください。
横200ピクセル、縦180ピクセルです。
ご利用の際は、ソースコードに
以下のscriptタグをコピー&ペーストするだけとなっています。

2009年10月8日木曜日

Rails 2.3.4 script/consoleでUrlHelperのメソッドを試す

久しぶりの投稿となりますが、
今日はRailsの最新安定板である2.3.4で
開発時において、とても便利なscript/consoleで
link_toをあらかじめ試す方法を書きたいと思います。

ご存知のとおり、RAILS_ROOTで
ruby script/consoleを実行すると、
コンテナコンテキスト外で
Railsアプリケーションを
実行することができます。

書籍などでは、モデルのファインダーメソッドを試す例などが
良く記載されていますよね。

例)p = Product.find :conditions => {:id => 2}
puts p

これは、script/consoleがirbを起動する際に、
Productというクラスをロードしてくれているおかげです。

しかし、ビューの中で頻繁に使用するヘルパーメソッドを
試すには、ちょっとしたHackが必要になります。

といっても、小難しいことは必要なく、
selfのhelperのインスタンス変数controllerに
appをセットするだけです。

helper.instance_variable_set('@controller' , app)

(※ここに記載されているself、helper、appなどの変数に関しては、
いずれ書く機会を設けたいと思います。)

すると、
puts helper.link_to 'click!' , :controller => 'dummy'
<a href="/dummy">click!</a>
というように、UrlHelperモジュールのlink_toメソッドを
試すことができます。いきなりhtml.erbに書くときよりも
安心感がありますね。

ちなみに、helperのcontrollerにappをセットしないと、
私の環境では、以下のようなエラーとなりました。

NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for
from c:/xxx/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb:85:in `send'
from c:/xxx/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb:85:in `url_for'
from c:/xxx/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb:228:in `link_to'
from (irb):1
(※railsをfreezeしている環境です。)

私の場合、開発時にはEmacsのシェルバッファで
script/consoleを実行しながら、シェルバッファで
うまく動いたコードを、ECB+rails_modeのソースバッファに
貼り付けるという単純な作業の繰り返しです。

Emacsから離れてブラウザでの動作確認を行って、つまらない
シンタックスエラーでエラー画面が表示され、またEmacsに戻って・・・
という負のサイクルを避けられれば、更なる効率アップに
つながると感じています。

2009年2月8日日曜日

Prototype.jsで作るちょっとCoolな多重クリック防止機能

Webアプリケーションでサーバサイドの言語を問わず、
アンカーのonclickハンドラを、何回もクリックされたくないときは
結構あります。
例えば隠しフォームをサブミットするjavaScript関数を、アンカーのonclickで
使用する場合など、多重に送信されるとサーバサイドの負荷を掛けるばかりか、
サーバ側の設計があまり良くない場合、データの不整合などが起きたり、
CSRF対策でワンタイムトークンを入れている場合など、初回のリクエストは
うまく処理できているのに、複数回クリックされてしまったために、
入力エラーのページに誘導しなければならないようなケースもあり得ます。

上記のような場合、各javascript関数に対して、個別に何かの制御を組み込まなければ
ならないと開発者の負荷も重くなり、テストも大変になりますよね。

Prototype.jsは1.6からFunctionクラスにwrapというメソッドが加わりました。
これはAOPで言うところのaround adviceにあたるもので、特定のメソッドを
インターセプトし、実行の前処理、後処理に後付で何か変更を加えることが
できるとても便利なメソッドです。

この機能を使用し、無形ソフトウェアLLCの公開ライブラリとして、
ckk_oocp.jsを作成しました。

以下のようなケースでこのライブラリは有効なのではないかと考えています。

ケース1:アンカーに対して、class属性で同一のセレクタを指定し、
    すべてのアンカーのonclickハンドラを1回しか実行させたくない場合。

ケース2:特定のアンカーに対して、onclickハンドラを1回しか実行させたくない場合。

まずは、ライブラリを読み込みます。

prototype.jsはprototypejs.orgから、
最新の安定板(1.6.0.3)をダウンロードし、以下のようにscriptタグを書きます。

次に今回の公開ライブラリをロードするようにscriptタグを書きます。
<script language="javaScript" type="text/javaScript"
src="http://www.chi-koku.com/javascripts/public_libs/ckk_oocp.js"></script>
(※現時点では直接リンクしてもかまいません。)

それでは、ケース別にライブラリの使い方を説明します。

ケース1:
<a class="only-one-click" href="#" onclick="submit_form_a();return false;">submit form A</a>
<a class="only-one-click" href="#" onclick="submit_form_b();return false;">submit form B</a>
<script>
var p = new OnlyOneClickProxy({selector:'.only-one-click'});
</script>

ケース2:
<a id="btn" href="#" onclick="submit_form_c();return false">submit form C</a>
<script>
var p = new OnlyOneClickProxy({id:'btn'});
</script>

一回のクリックでページ全体がロードされるなら、これでも大丈夫ですが、
Ajaxな場合は、XHRのレスポンスでもう一度onclickを復活させなければ
なりませんね。

その場合は、
p.restart();
p.restart(インデックス);
p.restartAll();
とします。
p.restartを引数なしでコールした場合、id指定されたアンカーや
セレクタで選択されたアンカーの先頭のonclickが復活します。
p.restartの引数にインデックスを指定した場合は、ページの先頭から
セレクタで選択されたインデックスのアンカーのonclickが復活します。
最後のp.restartAllは、セレクタで指定したすべてのアンカーのonclickが
復活します。

とても簡単ですね。
ちなみにアンカー以外でも、onclickハンドラを持っているHTML要素すべてに
適用することができます。これを応用してフォームのonSubmitなどに適用しても
良いかもしれません。

トータル50行弱のjavascriptですので興味のある方は読んでみてください。
バグのご報告がありましたら、こっそりとコチラのブログにお願いいたします。

2008年12月23日火曜日

年末調整事務・・・

 無形ソフトウェアLLCにとって
初めての年末調整がやってきました。

僕は会社員だった時期がそんなに長くないうえ、
10年くらい前のことなので、年末調整というと
少し給料を多くもらえるくらいの印象しかありませんでした。

今のところ「無形ソフト」として給与を払っているのが
「僕」ひとりですが、僕があるいは会社が、
いちおうの「給与の支払い者」ということに
なっているので、事務手続きをする立場に立った
年末調整は避けて通れません。

会社を設立してから、特にお役所関係に提出しなければならない書類を
書くときに、これは会社側の立場としてなのか、自分本人として
の立場なのか分からなくなるときがあります。

オブジェクト指向でいうと、オブジェクトがもっている
プロパティのgetterって、そのオブジェクトを主体で
見たときに、本当はsupplyあるいはgiveなんじゃないのかって
思うことが時々あるのと一緒ですね。
(だってメッセージのレシーバとしてオブジェクトに対するgetという
操作名は日本語でいうと"~を得ろ"ってことになりますから。で、
そのオブジェクトは「はい、得ますけど、それで?」みたいな・・・
「得てる」のはレシーバじゃなくて戻り値をもらってる
センダーでしょ?みたいな・・・)
話がややこしくなりそうなので、
妄想オブジェクト指向の話はこの辺で(笑)


それで、僕が給与の支払い者として川越税務署とふじみ野市役所に
出さなければならない書類がいくつかあります。
(#いまのところ、そうであると予想しています。。。)

<川越税務署>
・平成20年分 給与所得の源泉徴収票等の法定調書合計表
・僕の源泉徴収票(他に社員がいれば社員数分)
・そのほか法定調書5種類(必要な書式×全社員数分)

<ふじみ野市役所>
・給与支払い報告書(統括表)
・僕の給与支払い報告書(個人別明細書)

そのほか、20年分の
・僕の保険料控除申告書 兼 配偶者特別控除申告書
・僕の扶養控除等申告書
・僕の所得税源泉徴収簿
というのがあり、これは出すのかどうか分かりません。
まぁ初めてなので、他の書類と一緒にもっていって
聞いてみます。

役所{
 年末調整(){
  提出書類[] 書類たち = 無形ソフトLLC.get年末調整書類();
  for(書類 : 書類たち){
    チェック(書類)
  }
 }
}

わぁ、なんかの本で見たことある(笑)
でも深く考えなければ、すんなり出てくるのはgetなんだなぁ。