Upgrade to Pro — share decks privately, control downloads, hide ads and more …

レガシーWebアプリケーションの性能とコードの健全性をインクリメンタルに改善する / p...

レガシーWebアプリケーションの性能とコードの健全性をインクリメンタルに改善する / pepabotech-20211209

Kōhei Yamamoto

December 09, 2021
Tweet

More Decks by Kōhei Yamamoto

Other Decks in Programming

Transcript

  1. カラーミーショップのショップページとその課題 7 • 2021年3Q実績は約99.36% (※) ◦ ダウンタイム(ショップが閲覧しづらい時間)が約280分/月 • 理由 ◦

    アプリの性能問題によるDB高負荷、パブリッククラウド専用線の障害(約400分) ◦ あるショップへのアクセス集中が全ショップの閲覧に影響することが何度か発生 • 障害中はショップ回遊や注文が減りショップオーナー様の不利益に ◦ コロナ禍でのニーズを鑑みると、このままでは安心して使っていただけない (※) Slackの障害対応チャンネルで記録した障害検知〜復旧の時間をもとにおおよその値を計算 cf. インシデントレスポンスを自動化で支援する Slack Bot で人機一体なセキュリティ対策を実現する https://speakerdeck.com/hiboma/insidentoresuponsuwozi-dong-hua-dezhi-yuan-suru-slack-bot-deren-ji-ti-nasekiyuriteidui-ce-w oshi-xian-suru 課題: ショップページ稼働率の低下
  2. カラーミーショップのショップページとその課題 9 • 目標: 2021年4Qのショップページ稼働率を99.95%以上にする ◦ 約21分/月のダウンタイム • 方針 ◦

    “パフォーマンス改善に専属で取り組むチーム”を組織 (※) ▪ ショップページは@hrysd、@takapi、@kymmtからなるチームが担当 ◦ 「カートに入れる」ボタンが存在する商品詳細ページの95%tileレスポンス時間を改善の 指標として選択 ▪ 性能に関する変更のフィードバックを得るための指標としてわかりやすく、性能改善により 稼働率向上にもつながるため ◦ 性能改善作業の失敗による稼働率低下をコードベースの健全性向上や仕組みづくりで防止 (※) 安定したサービス提供のための改善レポート【2021年7〜9月】 - よむよむカラーミー https://shop-pro.jp/yomyom-colorme/80571 2021年3Qの課題を踏まえて改善する
  3. ショップページを例としたレガシー Webアプリケーションの改善 11 • アプリケーション: vendor supportedなPHP 5.4 + php-fpm

    ◦ プライベートクラウド上のVMで稼働 ◦ Webアプリケーションフレームワークなし ◦ Capistranoでデプロイ • RDB: Amazon RDS for MySQL (5.7) • KVS: Amazon ElastiCache for Memcached 現状のアーキテクチャ
  4. ショップページを例としたレガシー Webアプリケーションの改善 13 • 画面クラスはほぼトランザクションスクリプト ◦ データ取得、計算、テンプレート変数の設定までを担う関数を持つ ◦ ルーティング層で必要な画面クラスのインスタンスを生成し、その関数を呼び出すイメージ 現状のアプリケーション設計

    <?php switch($_GET['mode']) { case 'product': $view = new ProductView(...); $view->fetchData(...); break; case 'category': // … } $view->render(); <?php class ProductView extends BaseView { public function fetchData(...) { // データ取得、計算、テンプレート変数設定 // など該当の画面に関係する処理 } } 呼び出し 画面クラス
  5. ショップページを例としたレガシー Webアプリケーションの改善 14 • アプリケーションの95%tileレスポンス時間が約960ms ◦ “サイト表示が0.1秒遅くなると、売り上げが1%減少” という話から速いとはいえない • クエリ発行回数が多いという特徴

    ◦ スロークエリの発生 ◦ 一連のトランザクションスクリプトで同じ結果を取得するクエリを重複して発行 ◦ テンプレート編集機能で利用できる独自タグのためのさまざまなデータ取得にも起因 ▪ 商品詳細ページだけで使える独自タグでも30種類存在 技術的な課題: 性能
  6. ショップページを例としたレガシー Webアプリケーションの改善 15 • ロジックやクエリの重複が複数の画面クラス間で発生 ◦ トランザクションスクリプトのデメリットそのもの • クラスの構造やコーディング作法に起因して可読性に難あり ◦

    2,000行超えの巨大なクラス、文字列連結でベタ書きのSQL、処理内容と合っていない 関数名など • 低いテストカバレッジ ◦ 24.0% ◦ 画面クラスに対してPHPUnitでざっくり書かれたテストは存在 技術的な課題: 設計と健全性
  7. ショップページを例としたレガシー Webアプリケーションの改善 16 • 細かい粒度での改善と検証を繰り返して変更失敗のリスクを軽減 • 改善の1サイクルは1週間 ◦ 現状のアプリケーションの振る舞いを観察して、優先して改善すべき点を3つ選択 ◦

    1週間手分けして改善に取り組み、性能の状況を毎日確認 ◦ 作業してみて健全でないことに気づいたコードの改善も徐々にタスク化 • 健全でないコードの改善を並行して進めることで安全かつ高速な性能改善を実現 ◦ テスト追加、静的解析、稼働率維持の仕組み 進めかた: インクリメンタルな改善のサイクルを回す
  8. 性能の改善 18 • APM (application performance monitoring)ツールを利用するのが楽 ◦ 今回の取り組みではNew Relicを利用

    • 遅いエンドポイント実行のトレースを採取してくれるので、それらを確認 ◦ アプリケーションレベルのトレースである関数呼び出しにかかる時間を確認 ◦ 遅いエンドポイントで発生しているスロークエリを確認 性能改善できる箇所の調査
  9. 性能の改善 20 • Amazon ElastiCache for Memcachedでサーバサイドキャッシュ ◦ 当初から「イチオシ商品」など即時性が必須でないデータはサーバサイドでキャッシュし ていたが、ローカルのファイルにキャッシュを保存するCache_Liteという

    PEARパッケージを使った方式のまま運用 ◦ 今となってはショップページが稼働するVMは数十台あり、ローカルのファイルを 使うと高いキャッシュヒット率は望み薄なので、Memcachedにすべて置き換え RDBのIOの削減
  10. 健全性の改善 25 • 画面クラスのトランザクションスクリプトから改善するデータ取得ロジックを クラスとして切り出し • ある種のゲートウェイクラス ◦ 取得データに対応するSQLの隠蔽 ◦

    PDOのラッパー: データ取得、加工と結果のarray組み立て ◦ 複数の画面クラスから利用可能 • このクラスに対してPHPUnitでテストを作成 ◦ データ取得ロジックを細かくテスト ◦ 徐々に取り組みを進めてコードベース全体のカバレッジは24.0%から29.4%に増加 テストが書きやすく再利用しやすい構造にしながら改善する
  11. 健全性の改善 26 • 静的解析ツールで実装上のミスや型を実装時に発見してコードの質を向上 • レガシーコードに対しても使いやすいPhanを利用 (※) ◦ いきなりコードベース全体を検知パターンに沿うように直すのは難しい ◦

    既存コードではプリセットの検知パターンの違反をいったん無視 (baseline.php) ◦ 新規コードに対しては必ずチェック • 新規コードではPHPDocでプロパティや関数に型を記述してチェック • コミットpush時にCIで毎回実行 (※) cf. Tutorial for Analyzing a Large Sloppy Code Base · phan/phan Wiki https://github.com/phan/phan/wiki/Tutorial-for-Analyzing-a-Large-Sloppy-Code-Base 静的解析で実装時に問題を発見する
  12. 健全性の改善 27 • 性能改善の変更で稼働率を下げないように稼働率を維持するための施策も実施 • stagingデプロイ時に別リポジトリのE2Eテストを実行 ◦ RSpec+Capybara ◦ GitHub

    ActionsでデプロイワークフローからE2E実行ワークフローをトリガー 稼働率を維持するための改善 E2Eテスト ワークフロー stagingデプロイ ワークフロー 2. POSTリクエストでE2Eテストのワークフローをトリガー staging環境 1.デプロイ 3. テスト実行
  13. 改善の結果 31 • コード変更回数を増やしつつも、10/1から昨日12/8までの稼働率は99.91%まで 向上 ◦ 目標まであと0.04%…… ◦ 2021年3Qは20回デプロイだが、2021年4Qはすでに52回デプロイ ◦

    アクセス過集中での障害が減少し、3Qと比較してダウンタイムを約280分/月から 約39分/月まで削減 月平均稼働時間が約240分増加
  14. まとめ • レガシーなWebアプリケーションであるショップページの性能/健全性の インクリメンタルな改善に2021年4Qに2か月強のあいだチームで取り組んだ ◦ 95%tileレスポンス時間を約44%改善 ◦ 稼働率を99.36%から99.91%に改善 ◦ テストカバレッジ向上や静的解析で健全性を向上し、安全なコード変更を担保

    ◦ E2Eテストやロールバックの簡易化など稼働率を維持するための仕組みの導入 • 今後の課題 ◦ より本番環境に近い負荷試験環境 ◦ 大規模テーブルのクエリチューニングのための仕組みづくり ◦ PHPバージョンアップによる性能改善 ◦ デプロイ後障害検出時の自動ロールバック 33