パフォーマンス最適化

クライアントから「遅い」とエスカレーションを受けることは、アウトソーシング案件で最もストレスのある瞬間の1つです。ステージング環境では問題なかったのに、実際のトラフィック下では API レスポンスタイムが3倍になり、ユーザーから苦情が届いています。Sun Agent Kit のパフォーマンスエージェントはアプリケーションを体系的にプロファイリングし、N+1 クエリ・不足しているインデックス・ブロッキング I/O・メモリリークを特定します。そして、「キャッシュの検討」という汎用的なリストではなく、コードベースが必要とする具体的でターゲットを絞った修正を生成します。

概要

目的: パフォーマンス低下の根本原因を診断し、ターゲットを絞った修正を実装し、改善を検証する
所要時間: 30〜60分(手動プロファイリングと試行錯誤の場合は4〜12時間)
使用エージェント: debuggerscoutimplementertester
コマンド: /sk:debug/sk:scout/sk:cook/sk:test

前提条件

  • Sun Agent Kit がインストール・認証済みであること(インストールガイド
  • アプリケーションログへのアクセス、または低速リクエストのトレースを含むログファイル
  • クエリ分析のためのデータベースアクセス(読み取り専用の認証情報で十分)
  • 再現可能なシナリオ: 遅延を引き起こす URL パス・ペイロード・おおよそのリクエストレート
  • 任意: APM データエクスポート(Datadog、New Relic、またはアプリケーションレベルのタイミングログ)

ステップ別ワークフロー

ステップ 1: アプリケーションをプロファイリングしてボトルネックを特定する

症状(遅いエンドポイントの URL またはログの抜粋)をエージェントに与え、発生源を特定させます。

/sk:debug "API endpoint GET /api/products is returning in 3-8 seconds. Normal was 200ms. Traffic increased 3x last week."

実行内容: エージェントが以下を実行します:

  1. 症状の説明を解析し、影響を受けるエンドポイントと期待されるベースラインを特定する
  2. コントローラーからサービス、データベースクエリまでリクエストパスをトレースする
  3. N+1 クエリ・不足しているインデックス・無制限の結果セットを探してクエリパターンを分析する
  4. キャッシュレイヤーを確認し、ホットパスの同期 I/O を特定する
  5. 優先順位付きの指摘事項を含む根本原因分析を作成する

ステップ 2: コードベース全体の他のパフォーマンス問題を調査する

次のエスカレーションになる前に潜在的な問題を捕捉するために、調査範囲を広げます。

/sk:scout "N+1 query, SELECT *, missing pagination, synchronous I/O, no cache, unbounded loop"

実行内容: エージェントが以下を実行します:

  1. 一般的なパフォーマンスアンチパターンをコードベースでスキャンする
  2. 影響度で指摘事項を優先順位付けする(ホットパスの N+1 は HIGH、管理画面は LOW)
  3. plans/reports/ に保存される修正ロードマップを生成する

ステップ 3: Eager Loading で N+1 クエリを修正する

エージェントは、コードベースですでに使用されている ORM の慣用的な書き方に合わせて、JOIN または Eager Loading を使用するように影響を受けるクエリを書き直します。

/sk:cook "Fix N+1 queries in ProductController, OrderController, and UserController — use eager loading per the ORM conventions in this project"

実行内容: エージェントが以下を実行します:

  1. 使用中の ORM(Eloquent、Prisma、TypeORM など)を検出し、慣用的な Eager Loading を適用する
  2. 関連モデルを単一のデータベース呼び出しに含めるようにクエリを書き直す
  3. 結果が以前は無制限だった箇所にページネーションを追加する
  4. リグレッションがないことを確認するためにテストを実行する

ステップ 4: トラフィックの多いカラムにデータベースインデックスを追加する

エージェントはマイグレーション履歴を読み取り、外部キーや頻繁にフィルタリングされるカラムに不足しているインデックスを特定し、マイグレーションファイルを生成します。

/sk:cook "Add missing database indexes for performance — analyze the schema and generate migrations"

実行内容: エージェントが以下を実行します:

  1. 外部キー・WHERE 句カラム・インデックスのない ORDER BY カラムをスキーマで分析する
  2. 適切なインデックス定義を含むマイグレーションファイルを生成する
  3. 一般的なクエリパターンに対する各インデックスの影響を見積もる

ステップ 5: キャッシュレイヤーを実装する

トラフィックの多いエンドポイントに適切な TTL とキャッシュ無効化フックを持つ Redis キャッシュを追加します。

/sk:cook "Add Redis caching to GET /api/products and GET /api/categories — 5 minute TTL, invalidate on product or category write"

実行内容: エージェントが以下を実行します:

  1. プロジェクト環境で Redis の設定を確認する
  2. パラメーター化されたキャッシュキーを使用して指定エンドポイントにキャッシュアサイドパターンを適用する
  3. 書き込み操作(作成、更新、削除)にキャッシュ無効化フックを追加する
  4. キャッシュヒット・キャッシュミス・無効化シナリオのテストを作成する

ステップ 6: 改善を検証する

テストスイートを実行し、最適化が正しく動作することを確認します。

/sk:test "run all tests and verify no regressions from performance optimizations"

実行内容: エージェントが新しいキャッシュとクエリのテストを含むフルテストスイートを実行し、失敗を報告します。

完全な例: トラフィックスパイク後に API レスポンスタイムが低下

シナリオ

日本の EC クライアントの商品カタログ API は、パイロット期間中は問題なく動作していました。正式ローンチで数千人のユーザーが48時間以内に参加しました。2日目、クライアントの PM から Slack に連絡が来ます。「商品ページの表示に5〜8秒かかっています。一部のリクエストが完全にタイムアウトしています。至急対応してください。」 チームは本番環境が実際の負荷下でどう動作するかを見たことがありません。APM ツールは設定されていません。

連鎖コマンド

# 1. Diagnose — give the agent the symptom
/sk:debug "Product catalog API (GET /api/v1/products) degraded from 200ms to 5-8s after launch. Timeouts occurring under load. No APM data available."

# 2. Full performance audit to find all related issues
/sk:scout "N+1 query, missing index, missing pagination, synchronous I/O, no cache, unbounded loop"

# 3. Fix the highest-impact issues first
/sk:cook "Fix N+1 queries in product catalog — use eager loading per ORM conventions"
/sk:cook "Add database indexes on foreign keys and frequently filtered columns"

# 4. Add caching layer
/sk:cook "Add Redis caching to product list and product detail endpoints — tag-based invalidation on product write events"

# 5. Add pagination to prevent unbounded result sets
/sk:cook "Add cursor-based pagination to GET /api/v1/products — default page size 20, max 100"

# 6. Test everything
/sk:test "run full test suite after performance optimizations"

結果

エスカレーションから数時間後、API は負荷下でも高速なレスポンスタイムに戻りました。クライアントは具体的な根本原因と行われた変更を説明するレポートを受け取ります。PM は返答します。「迅速な対応をありがとうございます。次回は早めに気づけるようにモニタリングを追加してください。」 それはクライシスではなく、フォローアップのスコープ会話です。

時間比較

タスク手動Sun Agent Kit 使用
コードを読んで遅いクエリを探す60分数分
N+1 パターンを手動で特定する45分数分
インデックスマイグレーションの調査と作成30分数分
キャッシュレイヤーの設計と実装90分数分
キャッシュ無効化ロジックの作成30分数分
クライアント向けパフォーマンスレポートの作成30分数分
合計約5時間30分以内

パフォーマンス目標値テーブル

パフォーマンスチケットのクローズ時に受け入れ基準として使用してください:

エンドポイントタイプ許容 p50許容 p95最大 p99
リスト(ページネーション付き、キャッシュあり)< 100ms< 200ms< 500ms
リスト(ページネーション付き、キャッシュなし)< 300ms< 600ms< 1,000ms
詳細(単一レコード)< 80ms< 150ms< 300ms
書き込み(作成/更新)< 200ms< 400ms< 800ms
検索(全文検索)< 500ms< 1,000ms< 2,000ms
レポート / 集計< 2,000ms< 5,000ms< 10,000ms

ベストプラクティス

1. 最適化する前に必ず計測してベースラインを取る ✅

エージェントは修正を生成する前に症状の説明を求めます。プロファイリングをスキップして「Redis をあらゆる場所に追加」に飛びつくと、間違った箇所をキャッシュして本当の問題を隠すことになりかねません。具体的なメトリクスを要求してください。「50同時ユーザー下での GET /api/products の平均 3,200ms」は診断です。「なんか遅い」は診断ではありません。

2. キャッシュを追加する前に N+1 クエリを修正する ✅

遅いクエリをキャッシュすることは修正ではなく、問題を隠しているだけです。リクエストごとに大量のクエリを発行する N+1 は、キャッシュがミスするたびに依然として大量のクエリを発行します。書き込みが多いワークロード下ではキャッシュは常にミスします。まずクエリを修正し、その後データベース負荷をさらに下げるためにキャッシュを追加してください。

3. レスポンスタイムを短縮するためにデータベース書き込みをキャッシュしない ❌

すぐに成功を返して非同期で処理することで書き込み操作を「キャッシュ」したくなることがあります。これはキャッシュパターンではなくメッセージキューパターンであり、誤って実装するとデータ損失が発生します。非同期処理が必要な場合は、アドホックなインメモリキャッシュではなく /sk:cook "add job queue" を使用して適切なキュー統合をスキャフォールドしてください。

4. すべてのキャッシュデータに同じ TTL を設定しない ❌

商品名は変更頻度が低いため、10分の TTL で問題ありません。ユーザーのカートはインタラクションごとに変化します。10分間キャッシュするとカート消失バグが発生します。エージェントはエンティティタイプごとに保守的な TTL をデフォルト設定します。デプロイ前に書き込み頻度に基づいて見直し、調整してください。

トラブルシューティング

問題: インデックスを追加したがクエリが速くならない

解決策: 遅いクエリに対して EXPLAIN ANALYZE を実行し、インデックスが使用されていることを確認してください。Postgres と MySQL は統計が古い場合、新しいインデックスを無視することがあります。ANALYZE products;(Postgres)または ANALYZE TABLE products;(MySQL)を実行して統計を更新してください。それでもクエリプランナーがインデックスを無視する場合は、エージェントがクエリの WHEREORDER BY 句により一致するようにインデックスを再設計する手助けをします。

問題: Redis キャッシュは開発環境では動作するが、本番環境で古いデータが返される

解決策: 最も一般的な原因は、予想していなかった書き込みパスでのキャッシュ無効化が欠落していることです。/sk:scout "find all write operations on the products table" を実行して products テーブルに書き込むすべての場所を見つけてください。それぞれにキャッシュ無効化フックが必要です。エージェントが見落とされた箇所をリストアップします。

問題: 修正後も負荷テストでエラーレートが高い

解決策: 負荷下でのエラーレートの高さは通常、クエリ速度ではなくコネクションプールの枯渇を示します。データベースコネクションプールサイズを確認してください(.envDB_POOL_SIZE)。よくある設定ミスは、100以上の同時リクエストを処理するサーバーでプールをデフォルトの5コネクションのまま放置することです。出発点として (2 × CPU コア数) + ディスク数 に設定してください。

問題: 負荷テストでは問題ないが実際のユーザーには遅い

解決策: 同じエンドポイントとペイロードを使用した負荷テストは実際の変動を捉えません。/sk:debug "slow for specific users" を実行し、遅延が大規模データセットを持つユーザー(注文数が多い、カートに商品が多い)と相関しているかどうかを確認してください。修正は通常、user_id インデックスの追加、またはクロスユーザーデータを誤ってロードしているクエリのスコープ制限です。

次のステップ

  • コードレビュー — コードレビュー時にパフォーマンスアンチパターンを本番環境に到達する前に検出する
  • レガシーコードのリファクタリング — 構造的なリファクタリングにより、ポイント修正では届かない根本的なパフォーマンス問題を解消することが多い
  • セキュリティ監査・スキャン — 最適化によりセキュリティ上のリグレッションが発生していないことを確認するためにパフォーマンス作業と並行して実行する

重要なポイント: パフォーマンスエスカレーションが緊急に感じられるのはその通りだからです。遅い API から高速なレスポンスタイムへの最速の道は、推測して期待することではなく、体系的なプロファイリングに続くターゲットを絞った修正です。