1年間で取り組んだProttのパフォーマンスチューニング
これはGoodpatch Advent Calendar 2015の25日目のエントリです。
Prottというサービスではパフォーマンスチューニングのために、Railsアプリケーション/フロントエンドの改善、ミドルウェアのバージョンアップ、SPDYやHTTP/2の導入、オンプレミス環境からAWSへの移行などを行ってきました。 今回はその取り組みの一部をご紹介します。
Slides
10月にGoodpatch Engineer Meetupというイベントをやって、Prottのパフォーマンスチューニングについて話した。
イベントの内容はこちらにまとまっている。
僕のスライドはこんな感じ。
"Make the Prott Faster"ってタイトルにしたけど、theはいらないよってあとから教えてもらった。cute misstakeって言われた。感覚がわからない。
パフォーマンスチューニングで取り組んだこと
Goodpatchには2015年1月に入社して、そこからいろいろなことに取り組んできた。10月の時点で2倍くらいは速くなった。いまはさらに速くなっている。でもまだまだ改善できるので、来年もパフォーマンスチューニングに取り組んでいく。 2015年10月に計測したときはこんな感じだった。
イベントのあった10月までに、こんなことをやってきた。
- Infrastructure
- Rails Application
- N+1問題の解消(道半ば)
- Slow Queryの解消
- メモ化
- Cache(Russian Doll Caching)
- Frontend
- AngularJSバージョンアップ
- $watchの削減
11月から12月にかけて大きな変更をした。
僕は主にRails側の対応を行い、フロントエンドやインフラの対応はチームメンバーがやってくれた。SPDY導入や、$watchの削減についてはこちらの資料にまとまっている。
Prottの抱えていた課題
プロジェクト一覧の画面が遅い。
スクリーン一覧の画面が遅い。
このよく使うページを中心に改善していった。
パフォーマンスチューニングの指針
よく言われていることを、ちゃんと実践するようにする。
- 推測するな、計測せよ。
- 時期尚早な最適化は諸悪の根源だ。
- 1度に修正するのは1箇所のみ。
このあたりに詳しく書いてある。
ただこのときは、時期尚早どころか もっと早く対応するべきだった…。
SPDY
SPDYの導入にあたってはこんなことをした(いまはHTTP/2を使っているけど)。
- spdycatで接続確認する
- h2loadで計測する
このあたりを参考にさせていただいた。
spdycatを使うと、こんなふうに接続確認ができる。
% spdycat https://prottapp.com/ -v -n [ 0.379] NPN select next protocol: the remote server offers: * spdy/3.1 * http/1.1 NPN selected the protocol: spdy/3.1 [ 0.386] Handshake complete [ 0.387] recv SETTINGS frame <version=3, flags=1, length=20> (niv=2) [4(0):100] [7(0):2147483647] [ 0.387] recv WINDOW_UPDATE frame <version=3, flags=0, length=8> (stream_id=0, delta_window_size=2147418111) [ 0.387] send SYN_STREAM frame <version=3, flags=1, length=215> (stream_id=1, assoc_stream_id=0, pri=3) :host: prottapp.com :method: GET :path: / :scheme: https :version: HTTP/1.1 accept: */* accept-encoding: gzip, deflate user-agent: spdylay/1.3.2
h2loadで負荷を掛けられる。cでクライアント数、nで回数を指定できる。出力はこんな感じ。
% h2load -c 1 -n 1 https://prottapp.com starting benchmark... spawning thread #0: 1 concurrent clients, 1 total requests Protocol: TLSv1.2 Cipher: ECDHE-RSA-AES256-GCM-SHA384 Server Temp Key: ECDH P-256 256 bits progress: 100% done finished in 117.42ms, 8.51665 req/s, 1.29MB/s requests: 1 total, 1 started, 1 done, 1 succeeded, 0 failed, 0 errored, 0 timeout status codes: 1 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 158829 bytes total, 375 bytes headers, 158106 bytes data min max mean sd +/- sd time for request: 78.29ms 78.29ms 78.29ms 0us 100.00% time for connect: 38.74ms 38.74ms 38.74ms 0us 100.00% time to 1st byte: 90.82ms 90.82ms 90.82ms 0us 100.00%
開発環境でApache Benchとh2loadで測定したら、レスポンスタイムが10%程度改善した。あんまり厳密は計測していなくて、遅くなったりしないことだけ確認した。
MongoDBのSlowQuery
mongoidのexplainメソッドだとほしい情報が取れなかった(executionStatsを渡した結果がほしかった)ので、monogo shellでexplainした。
> db.xxxx.find({ deleted_at: null, screen_id: ObjectId('XXXXXXXXXXXX')}).sort({_id:1}).explain('executionStats') { "queryPlanner" : { ... "winningPlan" : { "stage" : "FETCH", ... },}, "executionStats" : { ... "executionTimeMillis" : 111, "totalKeysExamined" : 55555, "totalDocsExamined" : 55555, } }
winningPlanの内容を確認すると、stageがFETCHになってはいるけど、sortが効いてるだけっぽい。 検索したindexの数と検索したdocumentの数が同一になってしまっているので、indexは効いていなかった。
indexを追加することで改善した。
> db.xxxx.find({ deleted_at: null, screen_id: ObjectId('XXXXXXXXXXXX')}).sort({_id:1}).explain('executionStats') { "queryPlanner" : { ... "winningPlan" : { "stage" : "FETCH", ... },}, "executionStats" : { ... "executionTimeMillis" : 2, "totalKeysExamined" : 1, "totalDocsExamined" : 1, } }
Rails Application
計測するためにつかったツールとか。
使わなかったツールとか。
stackprofとstackprof-webnav
stackprof
stackprofはRubyのプロファイラ。作者はGitHubのAman Gupta(@tmm1)さん。RubyKaigi2014 3日目の基調講演のスピーカーでセッション終了後の拍手がものすごかった。
RubyKaigi2014でのスライドはこちら。
config.ruをこんな感じにするとRAILS_ROOT/tmpにダンプができる。
require ::File.expand_path('../config/environment', __FILE__) use StackProf::Middleware, enabled: true, mode: :cpu, raw: true, interval: 1000, save_every: 5 run Rails.application
stackprofで解析するとこんな感じ。
$ stackprof tmp/stackprof-cpu-*.dump --text --limit 1 ================================== Mode: cpu(1000) Samples: 780 (5.45% miss rate) GC: 88 (11.28%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 33 (4.2%) 27 (3.5%) Psych::ScalarScanner#tokenize
stackprof-webnav
stackprofの出力をWebで確認できる。ドリルダウンとかできて便利。
$ gem install stackprof-webnav $ stackprof-webnav -f tmp/stackprof-cpu-53222-1444801808.dump
こんな感じで表示される。
こんな感じでドリルダウンできる。
Frontend
画像の Layout / Paint が重かった。Chrome DevTools の timeline を使って計測。画像を縮小して表示している部分が重かった。
DevToolsのUI、カオス。そしてまたいろいろ変わっているんだろうな。Renderingタブにいろいろ設定がある。
いろいろ有効にすると、LayerやFPSを確認できる。
改善するために、試しにtransform使ってGPU使ってみた(ちょっと試してみたかった)。速くなったけど根本的な解決じゃないではないので、Nginx(ngx_small_light)で縮小したサイズの画像を生成することにした。
画像への初回リクエスト時に指定されたサイズの画像を生成しキャッシュするようにした。適切なサイズの画像を使うことで、ブラウザがかなりスムーズに動くようになった。
あとはこんなツールを使ったり、細かいJSの改善をやり続けていった。
AWS
これらの取り組みのあと、インフラをオンプレミス環境からAWSに移行した。地理的には東京からオレゴンに移行した。Prottをグローバルに使われるサービスにしていくために必要なことだった。とても難しい決断だった。
ヨーロッパでProttが遅いという報告を以前から受けていて、10月から11月にかけてベルリンに行って調査した。アメリカにサーバを置くと、かなり改善することがわかった。もちろん日本からのアクセスのレイテンシーは大きくなる。
そのため、AWS化にともないNginx / MongoDBなど主要なミドルウェアのバージョンアップを行い、さらに使うインスタンスのスペックを上げた。
その結果、日本からのアクセスのレイテンシーは増えたけどパフォーマンスは上がり、これまでよりも速くなった。ヨーロッパやアメリカでは格段に速くなった。
事前にしっかり準備することで、大きな問題も起こさずに移行を完了できた。海外のメンバーから、Prottが速くなった!というメッセージをたくさんもらえて、とてもうれしかった。
まとめ
フロントエンドの改善は id:yoshiko_pg が、がつがつ進めてくれた。インフラ周りのほとんどの作業を id:urapico が担当してくれた。他のメンバーもいろいろな改善をやってくれた。
経営的判断として、パフォーマンスチューニングを最優先課題として取り組んだ。チーム全体でパフォーマンスチューニングに 取り組む期間を作れたことはとてもよかった。
ただ、まだまだボトルネックになっているところは多いし、もっと速くしていかなくてはならない。例えばキャッシュで速くなっても初回アクセスの重さが解消できていないし、当たり前の設定を入れた感じなので もっと攻めたい。
Goodpatchは「ハートを揺さぶるデザインで世界を前進させる」というビジョンを掲げている。サービスが遅いと ハートを揺さぶれない。パフォーマンスは UXに直結する。
Prottをもっとよいサービスにするために、来年もいろいろなことに取り組んでいく。
We are hiring!!
Prottを改善していくエンジニアを募集しています!興味があれば気軽に連絡ください。
RubyKaigi2015に行って、Ruby 2.3.0-preview2をインストールした
今年もRubyKaigiに行ってきた!
去年はこんな感じだった。
初日のKeynoteの最中にRuby 2.3.0-preview2がリリースされた。
というわけでさっそくインストールしてみた。
Ruby 2.3.0-preview2のインストール
開発環境はMacで、homebrewとrbenvを利用している。homebrewのインストール方法とかは割愛。まだ rbenv や ruby-build をインストールしていない場合は、こんな感じでインストールする。
brew install --HEAD ruby-build brew install rbenv
すでにrbenvやruby-buildがインストールされている場合は、こんな感じでアップデートする。
brew update && brew upgrade --HEAD ruby-build rbenv install 2.3.0-preview2
ruby-buildのインストールやアップデートで --HEAD のオプションを渡す必要がある。これで最新のruby-buildを利用できる。このあたりのことは rbenv/ruby-build に書いてある。
brew upgrade --HEAD ruby-build brew install --HEAD ruby-build
続いてRuby2.3.0-preview2をインストールする。
rbenv install 2.3.0-preview2
ちゃんとインストールできたか確認する。
rbenv versions system 2.2.2 * 2.2.3 (set by /Users/sada/.ruby-version) 2.3.0-preview2
よさそう。rbenv globalで利用するRubyのバージョンを指定できる。
rbenv global 2.3.0-preview2
RailsアプリケーションのRubyバージョン変更
Prottでは .ruby-version を利用している。RAILS_ROOTにある .ruby-version のバージョンを書き換える。
2.3.0-preview2
ruby -v ruby 2.3.0preview2 (2015-12-11 trunk 53028) [x86_64-darwin15]
いい感じ。Rubyのバージョンが変わったので、bundlerをインストールする。
gem install bundler
rbenv rehash したあとに、bundle install で gem をインストールする。
rbenv rehash bundle install
ProttはRSpecを使っているので、テストを流して問題ないことを確認した。CHANGELOGから問題の起きそうな変更がないことを確認した。 サーバを起動して簡単な動作確認を行ってみましたが、特に問題は起きなかった。
プロダクションに投入するときにはもうちょっとしっかり確認するけど、だいたいはこんな感じ。さくっと動いてよかった。
&.(ぼっちオペレーター)や did_you_mean をちょこちょこ試した。楽しい。クリスマスに正式版がリリースされる予定なので、積極的にバージョンアップしていきたい。
まとめ
Goodpatch は RubyKaigi 2015 のゴールドスポンサー。まだ小さい会社だけど、好きな技術のカンファレンスのスポンサーになったり、こういう活動ができてうれしい。
もちろんRubyエンジニアを募集していて
そのほかの職種も募集している。
- デザインの力を信じるメンバーと一緒に働きたいエンジニア大募集!
- Goodpatchの成長を支える社内エンジニアを大募集!!
- デザインの力を信じるiOS/AndroidエンジニアWanted!
- Goodpatchの自社サービス「Prott」のUIデザイナー大募集!
- 世界140カ国以上で使われるProttの成長を加速させるディレクター募集!
- 世界で使われるProttの成長に関わりたいユーザーサポートメンバー募集!
興味があったら、気軽にぽちったり、連絡ください。
RubyKaigi 2016は京都で開催される。楽しみ。
Software in 30 Days スクラムによるアジャイルな組織変革"成功"ガイド
「Software in 30 Days スクラムによるアジャイルな組織変革"成功"ガイド」を読了した。
スクラムに関する本だけど、スクラム自体がテーマではなく、スクラムをどうやってチームや組織に適用していくのか、といった内容だった。
目次はこんな感じ。
勝手に本書の内容を分けてみると、こんな感じだと思う。
本書を翻訳された吉羽さんのエントリに、こんなことが書かれていた。本書の内容が端的に示されていると思う。
本書は、ジェフとケンにとって初の「ソフトウェア開発をしない人向けに書いた」本です。
<中略>
スクラムのプラクティスはそれほど書かれているわけではなく、スクラムの考え方、組織への適用にフォーカスをあてているので、開発チームの方というよりは、マネージャー層以上の方が読まれるといいと思います。
Software in 30 Days スクラムによるアジャイルな組織変革“成功"ガイド 発売のお知らせ | Ryuzee.com
なぜソフトウェア開発プロジェクトは失敗するのか
第1部では、なぜソフトウェア開発プロジェクトが失敗するのか、いろいろな事例をもとに解説されている。
スクラムの本は他にも読んでいたので、この章ではあまり新しい発見はなかった。ただ、いろいろな事例が紹介されているので、スクラムがうまく導入できない状況の人には、参考になることが多いと思う。
チームでのスクラム
第2部では、スクラムをどうやって適用していくかという内容になる。
第5章、第6章では、普通のスクラムの事例が紹介されている。数名のチームでのスクラムの解説で、基本的な考え方やスプリントなどのプラクティスがいくつか紹介されている。
組織でのスクラム
第7章から第10章、ここがいちばん面白かった。複数のスクラムチームで、どうやって巨大なソフトウェアを開発していくのか、スクラムを適用すことでどうやって組織を変革していくのか、といったことが、こちらも多くの事例をもとに解説されている。
第7章ではAdobe Premiere Pro CS3からCS5までの開発チームの変化が紹介されていて、そこが読み物としても特に面白かった。
Adobeのチームでは、個別のチームが開発を行いビックバン統合(リリース前にすべてを一気に統合する)でたくさんのバグがでてしまう状況から、スプリントごとにシステムを統合することで安定した開発ができるようになった。
第8章の組織や企業の変革に関する内容で、こんな一文があって印象的だった。
スクラムを組織全体に導入しようとする上級役員がいる場合、組織で最も変革を楽しみ、成功を得やすいのは、その上級役員である。
また第10章ではこんな文章があって、これは短い内容だけど納得した。
組織変革の際には、2つのスクラムチームを作る。
スクラムのやりかた
本文内で、スクラム自体の内容が少ない分、付録で補われている。スクラムガイドはPDFで読むことができる。
少ない量で、スクラムの解説がされているけど、短い分ちょっと分り辛い。スクラムを知っている人にはわかりやすいガイドだけど、スクラム自体を知りたいなら、ほかの書籍を読んだほうがよさそう。
用語集は短いけれど、わかりやすくまとまっていていい感じ。
まとめ
読んでたら、次のプロジェクトのメンバーのアサインや役割とか、こうしたらうまく回るんじゃないかとか、やりたかったあれができるようになるんじゃないかとか、いろいろアイディアがでてきた。
スクラムをちゃんと適用するのはけっこう大変で、自分のチームでもいくつかのプラクティスしか使っていない。もうちょっといろいろやっていきたいところ。
本書では旧態依然な企業が多く紹介されていた。今の会社は組織を作っている途中なので、組織変革というより、組織作りの真っ最中。だからいろいろなことが試しやすい。開発が必要なプロジェクトも多く、これから新しい問題がたくさん出てくると思う。それをうまくこなしていけるよい組織にするために、開発プロセスもちゃんと考えていきたい。