さいきんの Rails サービスを高速化をしてみた

先日のももクロハッカソンで出会った wantedly を作ってる仲さんが

と言ってたので、面白そうなので wantedly を速くしてみました。

ちなみにデータが数百万オーダーもなさそうなのに、どのページもログインすると2-5秒ぐらいかかっていたので、確実に速くできそうだなぁという感覚はやる前からありました。

アプリケーションサイドのチューニング

初心者*1にありがちな問題として

  • SQL に適切にインデックス張ってない
  • キャッシュすべき場所をキャッシュしていない
  • 無駄なデータを引きすぎてる

ことがよくあります。ので順に実装を見ていきました。

SQLに適切なインデックスを張ってない

張ってありました!びっくり!\(^o^)/

キャッシュすべき場所をキャッシュしていない

Facebook API を利用したアプリケーションなんですが、ユーザのデータの取得を毎回馬鹿正直に HTTP を叩いてとっていたため、適切にキャッシュを入れました。これで1秒強ぐらい速くなりました。

無駄なデータを引きすぎている

うっかり全件データ(!) を引いていたり、引かなくて良いデータを引いていたり箇所をログと New Relic *2から見ていって直していきました。これでログインユーザが1-3秒ぐらい速くなりました。
SQL のインデックス張ってない問題やこれの問題は、データが少ない当初は遅いと感じなくても、データが増えれば増えるほど徐々に重くなっていき、また開発環境では本番と異なるデータを使ってるため、データ量が少ないこともあるため、初心者には気づきにくい & どこを直したら良いか解らないといったことが発生しがちでしょう。

結果

約6倍速\(^o^)/


体感速度のチューニング

続いてユーザが実際に感じる速度を向上させました。

wantedly は heroku にあるためサーバが遠く、1ファイルの取得に 200ms-300ms のタイムラグが発生します。そのため、以下のアプローチをとりました。

リダイレクトは極力減らす

リダイレクトするとそれだけで200-300ms遅く感じてしまうため、よくクリックするリンクはリダイレクトで無く、URL の生成でちゃんとリダイレクト先を生成するように変更しました。

静的ファイルをまとめる

Rails 3.1 からは 3.1 の目玉機能の一つ、asset pipeline が利用できるようになり、何も考えなくともルールに従えば js / css ファイルを一つにまとめられるようになったため、Rails 3.0 系から Rails 3.1.0 にアップグレードして asset pipeline を有効化しました。なお Rails 3.1.0 化はコード上 Rails 本体を弄る等無理な書き方をしてなかったため、割とすんなりアップグレードできました。

なお、heroku で asset pipeline をうまく使うには memcached plugin を入れ

# production.rb
  config.serve_static_assets = true
  config.assets.compress = true
  config.assets.compile = true

と設定するとうまく動くと思います。

静的ファイルをブラウザにキャッシュさせる

いわゆる Expires / Cache-Control ヘッダの指定ですね。Rails 3.1 + Asset Pipipele + Heroku でうまくキャッシュさせるには

config.assets.digest = true

で asset の URL が内容が変更があったら変わるようにし(じゃないとキャッシュされ続けるため)

config.static_cache_control = "public, max-age=#{1.day}"

で、Rack の ActionDispatch::Static で Cache-Controle ヘッダをはかせるようにします。

結果

以前

以後

ちょっとわかりにくいですが、青のラインが250msぐらい速くなり、体感的にもロードが速くなりました。特に Heroku のようなサーバが遠い場合は効果がだいぶ出ると思います。

終わりに

これ以上の高速化には国内のサーバにする & 動的では無いコンテンツにフロントでもう一段階ちゃんと Varnish 等を入れキャッシュ設計するが必要になりますが、それをするのはコストがかかりすぎるので、やるとしてもサービスがだいぶ伸びてからになると感じました。

あとよく Rails アプリ遅いと言われてますが、200ms 前後の速度でしたらサービスがだいぶ大きくならない限りは、Heroku 等の PaaS でも簡単に維持できるなぁと改めて感じました。*3

あとあと、初めてちゃんと Heroku 触ってみたんですがすごく良くできてますね。ただやっぱり太平洋超えるのは日本向けのサービスだとするとつらいなー、とも感じたので Heroku の ec2 の東京リージョン版が出たらより日本でも流行りそうですね〜。

*1:仲さんはプログラミング初めて一年、Rails 初めて数ヶ月ですが、Ruby の実装から html / css / js ほぼすべて一人で wantedly 作ってすごい…

*2:Heroku 無料で New Relic プラグインが使えて便利

*3:200ms だと遅いという方も居ると思いますが、だいたい重いと感じるサービスはもっともっと処理コストがかかってると思うので…

第一回ももクロハッカソンに参加して Acme::MomoiroClover リリースしました

最近はかなこ推しになりつつある、週末エンジニアの secondlife ですこんにちは。
9/4(日)にももいろ週末エンジニアの方々と都内某所で、第一回ももクロハッカソンを開き参加し、Perl ライブラリの Acme::MomoiroClover を作りました。

何故今更 Acme::MomoiroClover を作ったかと云うと、今までに日本のアイドルの Acme シリーズは二つ、Acme::MorningMusume と Acme::AKB48 があります。その Acme が存在するアイドル2ユニットに共通していえることの一つに、どちらも紅白歌合戦に参加したことがあることが言えます。つまるところ、日本の Acme::アイドル が作られたのユニットは100%紅白出場しているため、ももクロちゃんの今の目標である紅白歌合戦出場をほんの少しでもサポートできたらと思い、験担ぎの意味を込めて作りました!!

使い方

至って簡単!

use Acme::MomoiroClover;
my $momoclo = Acme::MomoiroClover->new;

とふつうに使おうとすると

MomoiroClover is obsolete. Please use Acme::MomoiroClover::Z 

となって使うことができません…。(マシンの時刻が2011年4月10日以前なら利用できます)

というわけで今は Acme::MomoiroClover::Z を使いましょう。

use Acme::MomoiroClover::Z;
my $momoclo_z = Acme::MomoiroClover::Z->new;

APIAcme::MorningMusume とほぼ同じ*1ですが、各 member に say メソッドがあり、自分のカラーで発言することができます*2

use Acme::MomoiroClover::Z;

my @members = Acme::MomoiroClover::Z->new->members('active');
my $count = 0;
for my $member (@members) {
  $count = 0 if (++$count >= scalar(@members));
  $member->say($members[$count]->nick->[0] . "〜");
}

Acme::アイドル について

Acme::アイドルに新しい1ユニットを追加したわけですが、Acme::MorningMusume の作成者、id:antipop 御大にお言葉をいただきました。

  • 「本当に大好きな時期は、メンテナンスコストはどうってことないけど、メンバー変更の精神的ダメージがつらい」
  • YAMLなどでのクラス自動生成は邪道。一個一個メンバーのクラスファイルがないと愛がない」

とのことで、気を引き締めていきたいと思う所存であります。

ももクロハッカソンの週末エンジニアたち

BGM としてスピーカからももクロの曲を流しながら流しながら作業をしてたんですが、お気に入りの曲になるとみんな歌うたったりコール出したり踊ったりするのでノリノリで作業できて超楽しかったです!特にももクロちゃん自己紹介と怪盗少女の時はみんながみんなノリノリになって一体感あふれてわくわくすぎました、また開催したい!!1

*1:ほぼ id:antipop さん作の Acme::MorningMusume のコピペモジュールなので…

*2: ピンクは端末が 256色対応じゃないと表現できないので明るい紫でごまかしております…