こんにちは!CTO の @yutadayo です。
先日パフォーマンスチューニング会として、パフォーマンス面でボトルネックがあるエンドポイントの課題解消を行うイベントを社内で実施しましたので、そちらのレポートを書きたいと思います。
ISUCON (コンテスト運営側が用意した Web アプリケーションを、その振る舞いを変えずにパフォーマンスを向上させ、ベンチマークのスコアを競うチューニングコンテスト)を参考にさせていただき、チームでパフォーマンス改善のナレッジをシェアしつつ、課題になっている API の処理部分を楽しく改善するという趣きで実施しました。
イベント開催のきっかけ
スマートバンクでは、パフォーマンスMTGという名前で隔週に一度 B/43 を構成するシステムの各コンポーネントの性能が悪化していないか、SLO を満たせているか、アプリケーションで想定外のエラーがでていないかなどを、サーバサイドのエンジニアが中心となって確認する会を行なっています。パフォーマンスMTGでは下記のような点を確認しています。
- NewRelic を用いて各アプリケーションのメトリクスが悪化していないか確認
- Sentry でトラックしている各アプリケーションでのエラーの確認
- RDS Performance Insights を有効にしているRDSのパフォーマンス確認
- Redash で組んでいるダッシュボードでのアプリケーション固有(特定の決済の成功 / 失敗率など)のメトリクスについて確認
- SLO がエラーバジェットを消化していないか確認
といった形で見るべき項目の整備を進めており、MTGの中で上から順にファシリテーション担当者が事前に気になる点を確認しておくスタイルで実施しています。
このMTGで上がってきたパフォーマンスが少し悪化しているAPIを直すにあたって、折角なのでISUCONぽくチームで修正にあたると面白いのではないかというメンバーの発言がきっかけとなって今回の会が開催されることになりました。
当日のレギュレーション
- 共通の1 APIエンドポイントの改善にチームであたる
- 2 ~ 3人で1チームを組み、制限時間は4時間とする
- 評価は改善後のAPIエンドポイントの速度の結果を参考にするが、改善手法やアプローチの仕方なども考慮に入れて総合的に判断する
イベントの様子
お題となったAPIはアプリの起動時に呼ばれ、ユーザーの口座や各入出金毎の入金上限などを取得するAPIでした。B/43 は入金 / 出金方法など数種類をサポートしているため、個別に上限値を計算する処理とDBへの参照クエリーを何度か投げる構造になっていたため、レスポンスが遅くなってしまっていました。
各チーム毎に役割分担や時間配分を決めて取り組みつつ、スマートバンクでは本番のDBのコピーを個人情報等のデータをマスクした上で簡単に使えるようにするツールがあり、そちらを各チーム毎に用いながら検証を進めました。各チーム、ボトルネックになっている特定の関数を見つけることができ、それぞれが考える最適な方法で解消に向けてアプローチを行っていたのが印象的です。
結果発表
4時間経過後に、3チームそれぞれで対策した内容の発表を行いました、3チーム毎に観点が違ったアプローチで対応していたのが、非常に面白かったです。
チームA
ボトルネック箇所になっている処理の修正は時間内に難しいと判断して、対象箇所が返す値をユーザー毎にキャッシュさせる戦略のアプローチをとっていました。実際に実行されるクエリーの発行数自体は下がり明確に効果がありそうでしたが、キャッシュ時間のコントロールやキャッシュを破棄する処理を実装する必要があり、そのまま本番に投入するにはもう少し工数をかけないといけなそうでした。
チームB
該当のAPI自体の設計を見直して起動時に取得している情報を別のAPIとして切り出し負荷を下げる方が良いのではというアプローチをとっていました。確かにボトルネックの箇所で行っていた内容は、入出金毎の上限値をそれぞれの方法ごとに取得していましたが、各画面単位でそれぞれ必要なデータだけ取得する方法にすることで、頻度高く呼ばれるAPIの負荷を下げることができそうです。その場にいたクライアントチームにヒアリングしつつAPIの概要をまとめるところまでできていたのが良かったですね。
チームC
一番王道なアプローチで解決に当たっていたのがCチームでした、ボトルネック箇所の修正可能なクエリーを見直して入金方法毎の上限値を取得するクエリーを1本化、またユーザーの口座に紐付いている関連テーブルのデータを eager load することで、クエリーの発行数を抑えるなどのテクニックも駆使ししていました。キチンとベンチマークを回して Before / After の計測もしていたのがポイント高かったですね。
結果はアプローチ方法や、実際のチューニングの完成度を見てチームCの優勝になりました!
まとめ
いつもどおりなら、パフォーマンスチューニングは個別のメンバーをアサインして対処にあたるのですが、チームでイベント風に開催することで下記のようなメリットがあると感じました。
- パフォーマンスチューニングにおける各自のテクニックや調査する時の勘所などナレッジのシェアが行われた
- 修正箇所のドメインにおいて経験の浅いメンバーとベテランの間で仕様の共有が行われた
- 何より、チームでコンテストっぽく取り込むことでパフォーマンスニューニングに前向きに楽しく取り組むことができた
例)披露された Rails Tips例
- Rails console 上でクエリーキャッシュを使えるようにする
ActiveRecord::Base.cache do ... end
- API を直接叩かずに Serializer を直接呼ぶことでAPI の挙動を確認する
Api::V1::XXXXXSerializer.new(hoge, fuga).to_json
レポートは以上となります、パフォーマンスチューニングを個人でもくもくと取り組むのもいいですが、チームで取り組むメリットもあるなと感じることができたので、また機会をみつけてトライしていきたいと思います!