i3Systems Engineering blog

株式会社アイキューブドシステムズの製品開発メンバーが、日頃のCLOMO開発の様子などを紹介します。

CLOMOのパフォーマンス改善の取り組みについて紹介

f:id:arikawa-i3:20200917102610p:plain アイキューブドシステムズでサーバサイドエンジニアを担当している s-arikawa です。

今回は弊社のCLOMO MDMシステムで現在取り組んでいるパフォーマンス改善プロジェクトの一部をご紹介させて頂きます。

私達がどのようにプロジェクトを計画し、遂行していっているか参考になれば幸いです。

プロジェクト発足の経緯

私達のMDMサービスも立ち上げから10周年を迎え、段々と数万台のデバイスを管理する「大規模ユーザー」が増えてきました。 そんな中、100~1,000台のデバイス管理では感じなかった利用者のストレスが顕在化してきています。

ストレスの最たるものは「Web管理画面が遅い」というものです。

CLOMOの本番環境をKubernetes化した話などでも紹介しましたが、CLOMO MDMではWebコンソール「CLOMO PANEL」を使ってWebブラウザからデバイスを管理します。 私達の直接のユーザーである企業の情報システム担当者様は、このPANELを主に使うことになるので、PANELが遅いのは大変問題です。

今までも小規模な改善は行われていましたが、「数万台のデバイスを管理したいお客様にも安心して導入してもらえるように、大きく効果がわかるレベルで改善をしたい!」と営業のメンバーから企画提案があり、大規模顧客向け機能改善プロジェクトが発足しました。

プロジェクトのスコープ

まずは、速度改善が必要な画面の調査を行って、「どの画面をどこまで早くするか?」の要件・スコープを定義していきました。

画面の調査には「Kibanaで利用状況のグラフ化」をして活用し、対応すべき箇所を特定していきました。 また、開発チームから松・竹・梅プランを提示して、今回どこまでやるかの議論の土台を作成しました。

それぞれ少し紹介させて頂きます。

Kibanaで利用状況のグラフ化

弊社のサービスのログはElastic Cloudに統合されていて、Kibanaでグラフを作って分析できる環境にあります。

今回は、PANELのサーバログを解析して、画面表示までに時間がかかっている処理を洗い出しました。 赤いところが遅いリクエストです。 縦軸がController#actionで、横軸にカスタマー名が表示されています。(ほとんどマスクしていて何もわかりませんが…)

このグラフで何がわかるかというと、「遅くて多くのお客様に使われている処理」がわかります。 このグラフを見て改善対象のあたりを付け、実際のコードを調査していきました。

画面ごとに松竹梅プランを作成して要件のブレイクダウン

企画提案段階では「遅い画面を早くして欲しい」といった要件で結構漠然としています。 そこで開発チームで改修プランを"松竹梅"に整理して提案しました。

  • 梅 : 対応が簡単で効果もそこそこある改善プラン
    • N+1問題の解決
    • 不要なクエリの削除
  • 竹 : 梅対応に加えて、それなりに工数が必要な対応を行うプラン
    • ロジックの高速化対応(非効率な処理の改善)
    • 画面表示で必要のない項目の取得をやめる
  • 松 : 高速化のために理想のアーキテクチャへ変更するプラン
    • DBを機能に合ったものへ移行を検討する
    • 画面表示処理にて、遅延ロードやプリフェッチを行う

イメージ図 イメージ図

このように整理することで、「費用対効果」を提示することができました。

企画してくれた営業チームや、経営メンバーとのディスカッションでも議論がスムーズに進み、スコープやプロジェクトの方針を決めることができました。

結論として、複数のリリースにわける「フェーズプラン」を取ることになりました。 このフェーズはここまで改善して、リリース、次のリリースは…と松竹梅の項目を拾っていくようなイメージです。

そして、8月3日にフェーズ1の本番リリースが完了しています。

フェーズ1でやったこと

フェーズ1では、「すぐできることをやろう」をテーマに、以下のような基本的な処理速度の改善を行いました。

  • 画面表示に必要のないSQLクエリを削減
  • 一覧画面の検索で起きているN+1問題を解消する
  • バックグラウンドジョブ並列数・優先度の最適化

ここから実際にどんな改修をしていったかを紹介します。

画面表示に必要のないSQLクエリを削減

たとえば、こんなムダなSQLクエリを発行していました。

  • 表示していないのに全体の件数をカウントしている。
    • 「全XXXXX件のうち1〜100件」というような表示を過去はしていたが、現在では表示していない為完全に不要。
  • 詳細画面の検索処理を一覧画面の検索処理で流用していて、一覧では表示していない情報まで取得している。
    • 一覧画面で必要な項目だけを取得するように書き換えることで高速化できる。

まずは既存の検索処理を精査し、そういった必要のないSQLクエリや項目を洗い出し、画面仕様の調査・整理からはじめました。

この時、Confluenceに調査ページを都度作成して、既存の検索処理で取得していた項目と本来表示に必要な項目となど、調査した内容を残しておくようにしました。 資料を残しておくことで、実装時やPull Requestのレビュー時に役立ちました。

一覧画面の検索処理にあるN+1問題を解消する

Railsで検索処理が遅いと思ったらとにかく確認すべきなのがN+1問題です。

CLOMO PANELでは、デバイスやユーザー・組織の検索でN+1が発生してしまっている箇所がいくつかありました。 開発環境ではデータが少ないため気づきづらく、本番環境でデータが増えてくるととたんに処理が重くなるといった困った問題が起きます。

改修方法は比較的簡単で、Railsの基本ともいえますが、preloadすることでほとんどのN+1問題を解決できます。

こんな感じです。

Device.where(device_type: :iPhone).preload(:applications, :installed_profiles).all

preloadを書いてあげると、Rails(Active Record)がapplicationsテーブルやinstalled_profilesテーブルを効率の良いクエリで検索してくれるようになります。

また、今後の開発でもN+1問題を起こさないためにBulletというgemを追加しました。

github.com

BulletはN+1問題が起きそうな場合に警告メッセージをログに出力してくれます。

バックグラウンドジョブ並列数・優先度の最適化

最後に検索処理とは別のところで起きていた問題です。

画面などから処理を呼び出したときに、時間のかかる処理はバックグラウンドジョブとして非同期処理にしています。 たとえばCSVインポート機能などの一括更新処理などがバックグラウンドジョブになっています。

そのジョブ化される処理の一部で、一度に大量の優先度の高いジョブが発行されてしまう問題が起きていました。 優先度の高いジョブが大量に登録されると、ジョブを処理するワーカーが専有されてしまい他のジョブが処理待ちで待機してしまいます。

ワーカーはKubernetes上で多重化され、待機ジョブの数だけ自動で増減する仕組みがあるので致命的な障害にはならないのですが、待たされたジョブの完了は少なくとも遅くなります。

待機ジョブの数だけワーカーを増やす (詳細はCLOMOの本番環境をKubernetes化した話を参照してください!)

今回は、不必要に大量のジョブを登録していた処理の見直しと、ジョブの優先度を適切な値に見直しました。

改善の結果

フェーズ1の改修は、2020/08/03に本番環境へリリースされました。 今回の改善ではびっくりするような速度向上にはなっていませんが、多くの画面で2~3秒早くなっています。

大規模顧客向け機能改善プロジェクトはこれで終わりではなく、今後も継続的に改善を進めていきます! 次のフェーズでは一部の画面のバックエンド・アーキテクチャを大きく変えて、「感動的な速度改善」を目標にしています 😊

リリースされた暁にはまたこのブログでどんなことをして速度改善しているのかご紹介できるかと思います。

今後もCLOMO MDMサービス全体のパフォーマンス改善を進めていきます。