横断的なチームを作ってパフォーマンス改善をやっている話

  • 52
    いいね
  • 0
    コメント

こんにちは、freee Engineers Advent Calendar 20日目担当の foostan です。

皆さんは普段からアプリケーションのパフォーマンスを気にしながら開発や運用を行っているでしょうか。気にしているという方は日々の監視や、いざパフォーマンスが落ちたときの対応などはどのように行っているでしょうか。

freee にはパフォーマンスを監視、改善するためのチームがあり、短中期的な目標を掲げて日々アプリケーションやインフラ、開発環境などの改善に努めています1

本記事ではそんなチームを作った経緯や実際にやってきたことをご紹介します。

チームを作った経緯

遅いものはなんでも速くしたい

私が入社して1年ほど経ちますが、毎日のように新しい機能がバンバンリリースされている裏側で、ある程度アプリケーションのパフォーマンスが犠牲にされているケースを垣間見ることがありました。また各種サービスを取り巻く開発環境やCI、デプロイなど「速度」に関する改善点はいくつも見られ何かと「あー遅いな」と思うシーンがありました。

その一方で同じように遅いと感じているエンジニアが沢山いて「直したいけど手を付けられていない」という状況であることを把握しました。もちろん改善を行ってきた方も沢山いましたがアプリケーションの成長に追いついていないようでした。

パフォーマンス改善は組織的に継続する必要あり

たまたま所属していたチームのタスクとしてパフォーマンス改善を行う機会があり、監視する環境を整えたり、いくつか改善をしました。ただ時間は限られており、その中でできることも限られていました。そこでもっと多くのエンジニアが参加して知見を共有しながら改善していく体制が作られるといいんじゃないかって考えはじめました。

メンバーは全員兼務、気になっている人が気になっていることを改善する

チームを作ると言ってもあまり大げさな話ではなく、今まで改善を行ってきた方や、共通の問題意識を持っている方、パフォーマンス改善に興味のある方などを巻き込むべく下記のような投稿をしたぐらいです。

image

あとはslackに #isu という部屋を作って情報共有や議論する場を設けました2
image

発足した当初の活動内容は

  • パフォーマンス確認したり
  • 遅い箇所(特定のページや動作)の報告したり
  • 改善したところ/しようとしているところ共有したり
  • エンジニア全体向けのレポート書いたり

など、何をやるかは基本的にその人の自由で、「何かやったらみんなで共有し合おう」ぐらいのゆるふわな感じでした。

現在はすこしだけちゃんとしていて、Q単位の目標をざっくりと決めて、週次で報告会を開いています。
だた、基本的にやることはいままで通り個々の自由です。(本業の)チームによってはこのための工数を確保して、本腰を入れてやってたりもします。

やっていること

現在やっていることを簡単にご紹介します。

パフォーマンス監視編

ざっくり監視

New Relic APM および New Relic Browser によってプロダクション環境をざっくりと監視しています。特に New Relic Browser の Page views ではどのページが遅いか確認でき、「Web application」、「Network」、「DOM Processing」、「Page Rendering」の4つの項目の推移を見ることができるので重宝しています3

なお New Relic のデータ保存期間は Pro 版でない限り制限があります。また Browser については Pro 版であっても最大 90 日しか見れません。過去の推移が見えないと中期的な目標も立てづらいので freee では必要なデータはAPI経由で取得して Redshift に保存、Redash で可視化しています。

主要なページ単位でグラフ化して週次の報告会で確認しています4
image

各項目におけるページ単位の推移もグラフ化しています。
これは主要なページにおける DOM Processing の推移です。

image

これらのデータは社員であれば誰でも見ることはできますが、全エンジニアに意識してもらうために毎朝ボットがつぶやいたりもします。

image

パフォーマンスが低下していたら #isu に移動して原因を調査したり、目標値を達成したときはこの場でお祝いしたりします。

詳細な監視

freee は基本的に Rails on Rails を使っており、アクション単位でログを取って Redshift に保存、 Redash で可視化できるようにしています。データは色んな観点で取得しており社内で広く使われていますが、パフォーマンス監視の観点で言えば、各アクションのレスポンスタイムをユーザ単位で見ることが出来ます。

こちらは主要なアクション別のレスポンスタイムの推移です。
週次の報告会で確認していて、変化があったときや新しい機能が追加されたときなどのパフォーマンス変化にすぐに気づけるようにしています。

image

パフォーマンスはデータ数によって左右されるのでサービスの性質上、ユーザ毎に大きくことなる場合があります。freee ではユーザを幾つかのグループに分けて各パフォーマンスの変化を監視しています。

image

サーバ監視

Mackerel と Prometheus を併用していてサーバの変化を監視しています。

Zabbix から Mackerel へ #mackerelio

次世代監視の大本命! Prometheus を実運用してみた

スロークエリ監視

監視方法については割愛しますが、スローログが発生すると権限を持っている一部のエンジニアにメールが飛んで来るようになっています。週次の報告会で発生状況やその対応などの共有を行っています。

ユーザの声

監視とは若干観点が異なるかもしれませんが、パフォーマンスに関して実際にユーザから意見を頂くこともあります。また社内のサポートやセールスの方々はエンジニアよりも実際にサービスを利用することが多く、そちらからも意見を頂きます。最近では我々の活動がエンジニア以外にも浸透してきており、事前にパフォーマンスに関する相談を受けたりなど活発に交流しています。

パフォーマンス改善 backend 編

N+1

初歩的ではありますが、気をつけないといつの間にか発生していたりします。
N+1 の発見には Bullet を利用しており、下記のように開発環境で検知できるようにしています5

development.log
2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts;    model: Post => associations: [comments]·
Add to your finder: :include => [:comments]
2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
/Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
/Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
/Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
/Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'

例えばこの場合、言われたとおりにすると

posts.includes(:comments)

のように直せます6

認証サーバに対するN+1

freee ではいくつかサービスをリリースしていますが、全てのサービスは共通の認証サーバを経由して認証を行います。また、認証サーバは認可に必要なロール情報を扱っているため、認証時以外でも問い合わせを行うことがあります。

例えば

users.map do |user|
  user.role # 裏では認証サーバへの問い合わせが走るとする
end

のような実装がある場合、users.size 分、認証サーバへの問い合わせが走ります。 これは DB に対する N+1 よりもインパクトが大きく見逃してはいけません。

この場合、users を取得するときに role を初めから付与するか、もっと複雑な内容であれば、users を渡して一括で role を取ってくるような API があればよさそうです。

SpeedGun

Rackアプリケーション用のプロファイリングツールです。
下記のように行単位で結果を見れるためどこが遅いか特定するために重宝します7
image

Ruby / Rails のバージョンアップ

freee ではなるべく最新のバージョンに保つようにしています。
セキュリティパッチに即座に対応するのはもちろんですが、パフォーマンス改善目的もあります。

スロークエリ改善

発生したものは粛々と直します。
また ActiveRecord を利用していると予想だにしない SQL が投げられる場合があるので、PRレビューなどでどのような SQL が発行されているか確認するようにしていて、出来る限り発生しないように気をつけています。

パフォーマンス改善 frontend 編

ファイル分離

最近のフロントエンド事情については こちら が詳しいですが、全ての js が結合された application.js が存在し、パフォーマンス面においても問題を抱えていました。現在は必要な js だけロードしていて、主に DOM Processing の値が大きく改善しました。

こちらのグラフは主要なページにおける DOM Processing の推移です。とあるページにおいて大きな改善が見られます。

image

外部サービスの精査

freee ではマーケティングツールやパーフォーマンス監視ツール、チャットツールなど多くの外部サービスを利用しています。またマーケティングツールには Google Tag Manager を利用しているため、エンジニアを通さずに自由に追加することができるようになっています。

適切なサービスを使っている分には良いのですが、外部サービスも障害を起こしたり、フロントの処理をブロックしたりするものもあるので利用する上で精査は必要になってきます。

現在はまだやってませんが、近いうちに外部サービスが悪影響を及ぼしていないか監視する仕組みを整えようと思っています。

テスト改善

サービスのパフォーマンス改善に限らず、テストの高速化などもやっています8
テストは PR を Push するたびに Jenkins で回していて、結果は Redshift に保存し、Redash で可視化しています9
image

これによりテストが遅くなったとき、原因が一目瞭然になります。
また遅いテストがはっきりするので、思い立ったときにすぐに始められます。

テストは parallel_tests で並列化していますが、1台で処理するには限界を迎えてきているので、複数台でスケールするような仕組みにしようと考えています。

まとめ

freee で現在取り込んでいるパフォーマンス改善について簡単に説明しました。

ボランティアベースでも回ることもありますが、チームのようなものを作って多勢で攻めていく姿勢は大事だと思っています。また、このようなチームがいることで他のエンジニアやエンジニア以外の社員もパフォーマンスを気にするようになってきており、いい傾向にあると言えます。今後もユーザに対して最速でマジ価値を届けるべくパフォーマンス改善を続けていきます。

ところで、freee では「遅いものはなんでも速くしたい」「非効率なものを徹底的に効率化したい」エンジニアを広く募集しています、気になる方は是非!!

さて、明日は固定資産のヌシ @tabachain です、お楽しみに!


  1. 突発的な速度低下などの対応を行うこともありますが、それはもはや障害であり、それを起こしたエンジニアがすぐに対応をするのでスコープにはいれてません。 

  2. 名前は ISUCON から取ってきています :pray:  

  3. 各評価軸については こちら が詳しいです。 

  4. 縦軸は秒、横軸は時間です。数値は意図的に消してます。 

  5. https://github.com/flyerhzm/bullet より転載 

  6. 実際は周辺のコードによって直し方は色々あると思います 

  7. https://github.com/zeny-io/speed_gun より転載 

  8. 遅いものはとにかく速くするのがモットー 

  9. 一連のフローは Digdag で書いています。詳細はまたの機会に Jenkins + Digdag については以前こんなものを書きました。