@ijin

[Michael H. Oshita]

第4回チューニンガソン(Tuningathon)で優勝してきた

3回目の参戦となる#tuningathon@tnmtさんと共に優勝してきました。

やった事は相方のブログに書かれているので、補足。

開演前

朝起きると、やたらとアラートが飛んでいるので調べると、うるう秒のせいでサーバ達が高負荷状態に。 @tnmtさんも同じ原因で障害対応中で待ち合わせ時間には間に合わず、参加が危うい感じ。 自分の方はなんとか片付けて、ぎりぎり開演前に到着。

お題発表

前々からやって欲しかったRuby on Rails! で、Refinery CMSというブログのチューニング。
内心喜びました。

前半戦

作業開始前にはまず何よりもバックアップ。 いつでも環境を戻せるようにRailsのフォルダをコピーしてMySQLのdumpを取っておく。

速攻でrbenv + ruby-buildを入れて、rubyの最新バージョン(1.9.3-p194)をインストール。 ビルドの間、まずは環境把握。システムのパッケージ版ruby 1.8.7が入っている事を確認して、 設定フォルダ(tuningathon/config)配下をさらっと目を通す。 my.cnfも見て全然パラメータ設定されてないけど/var/lib/mysqlの容量をチェックしたらほぼ空なので、 ここのチューニングはあまり効果が望めずと判断。やるなら後回し。

ビルドが終えた頃にデフォルト状態のままで動かして、まずベースとなるベンチマーク。 大体こんな感じでした。

topで見ると完全にrubyがCPUを専有しているので、まずそこから着手することに。

次にdevelopment.rbのキャッシュ周りのパラメータをいじる。 development modeだとソースの変更が即時反映・確認できるよう、毎回クラスをロードしているので遅いんです。

config.cache_classes = true
config.action_controller.perform_caching = true

これでだけでスコアが倍に。

Score: 5.255 (get=2.900, comment=2.355(3), check=1.000)

次にruby 1.9.3-p194で動かして計測し、6超え。

Score: 6.102 (get=3.600, comment=2.502(3), check=1.000)

この頃にやっと相方が合流し、予めGoogle Docsに書いてあった作業ログを共有。 unicornへの置き換えを試しみる事に合意。 設定ファイルを作ってもらっている最中に、jrubyへの置き換えを試すが、 制約上Gemfileの変更が不可だった為、mysql2のgemを外す事ができずDB接続ができなかったので断念。

unicorn単体で動かすと9ぐらい出たので、worker数を変えたり、 unicorn_rails gemからRails 3で推奨されている素のunicorn gemを試したりして、16-21。

Score: 16.205 (get=9.900, comment=6.305(7), check=1.000)
Score: 19.869 (get=13.000, comment=6.869(7), check=1.000)
Score: 21.812 (get=14.000, comment=7.812(8), check=1.000)

ベンチを取り続けると、commentがどんどん溜まって行くので viewのrender時間が増えてスコアが落ちたりしたので、毎回DBを初期化したのとRailsを再起動する事によってスコアが安定しだす。

最後にrubyのガベージコレクションを切ったりして、この時点での自己ベストがこんな感じでした。

Score: 29.733 (get=19.000, comment=10.733(11), check=1.000)

後半戦

rubyで出来そうなところは一通りやったので、次は前々から構想していた Railsの前段にReverse Proxyを置く事にチャレンジ。Varnishを入れて設定し始める。

ちょうどこの時期にTL上で@netmarkjpさんが1000超えのスコアを呟きはじめるので、 なんらかのReverse Proxyを使ったんだと確信。

実はこの時、1000超えのスコアはこちらでも出てたけど、たまに1とか0が出る不安定さだったので、 呟きませんでした。(運営側に計測されなかったのは別port<Varnishデフォルトの6081番>で試していたから)

後は黙々と設定をいじりつつ、相方に念の為DBのテーブルにインデックスを張ってもらったり、 ベンチマークスクリプトを解析してもらって、適切な設定を探るべく試行錯誤な感じ。

16時からの公式計測 段階ではまだスコアが安定してなかったので、ひとまず入賞が確実なrubyオンリーな構成で 2〜3回来るのを確認。その後、終了20分前になんとか安定したので一気にportを置き換えて、 DBを初期化し、railsを再起動して待ちかまえる。次の計測でそれまでトップだった@netmarkjpさんを逆転したようです。

まだ若干時間が余っていたので、Varnishのスレッド数等をいじったりして、17時終了の30秒前に設定を入れて再起動。 後から知ったのですが、この設定がどうやら効いたようで1番最後の計測でさらにスコアが跳ねた模様。 最終的なスコアは「1351.54」でした。

ちなみにローカル計測ベスト(3200.23)だとこんな感じだったけど、リモート経由だとネットワーク(?)がボトルネックになって そこまで出ませんでした。

varnishの設定ファイルはこんな感じ。 TTLを強制有効にして、POST後にいかにキャッシュクリアさせるかがポイント。 じゃないと、チェックの成功率の2乗でペナルティが課され、すごい勢いでスコアが減点されます。

/etc/sysconfig/varnish


/etc/varnish/default.vcl

世に出てるの設定例はVarnish 2系が多いので、3系はsyntaxが違うので注意。 コメントアウトされたコードはデフォルト挙動なので、記述されたのとマージされます。 @netmarkjpさんの設定はvcl_fetch()内でキャッシュクリアしてるけど、 Varnishのフロー的には 一番最初に呼ばれるvcl_recv()内でやった方がより効果的です。

まとめ

  • Railsは比較的遅いので、いかにヒットさせないかを考える
  • でもRubyのチューニングも重要。多分これが2位との差
  • ベンチマークスクリプトをよく読んで動きやスコアリングを把握する
  • 運営側はチャレンジングな事は先にやっておけと推奨していたけど、まず堅実なチューニングをして最後の冒険が良いかと。
  • 最後まで諦めない気持ち

おまけ

翌日、出社したら何故か優勝記念と称してMountain Dewが10本贈呈されてましたw

tuningathon優勝記念

最後に運営の皆様、ありがとうございました!

Comments