backend-perf について
バックエンドコードのパフォーマンス項目を点検し、各項目の適用可否と判定を示すスキルです。開発者向けで、一般的な項目と PostgreSQL 固有の項目を分けて扱います。
主な機能
- 01独立した非同期呼び出しの並列化
- 02逐次的な N+1 クエリの検出
- 03ページネーション件数クエリの確認
- 04PostgreSQL のインデックス網羅性確認
- 05STABLE 関数の再利用確認
- 06競合状態への対処確認
活用シーン
- 01マージ前のバックエンド変更レビュー
- 02PostgreSQL のクエリプランとインデックス監査
- 03重複取得や過剰な列取得の発見
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bhagyamudgal/ skills --skill backend-perfインストール後、Claude Code で/backend-perfを入力して読み込まれたか確認します。
詳細
バックエンドのパフォーマンスチェックリスト
以下の各チェック項目について、手元のコードを確認し、それぞれの判定を明記します。適用済み、問題なし、または該当なしとその理由を示します。記載していないチェック項目は、実施していません。
[pg] のマークが付いたチェック項目はPostgreSQL固有です。他のストアでは、翻訳せずにスキップします。
- 非同期処理の並列化。互いに独立した非同期呼び出しは、1つずつ
awaitせず、Promise.allで同時に実行します。 - N+1クエリ。逐次実行される
forまたはfor awaitループ内でクエリを実行しません。代わりにIN句または結合を使ってまとめます。インデックスに適したクエリを数件実行するPromise.all(items.map(q => oneQuery(q)))は並列処理であり、逐次的なN+1ではありません。ファンアウトには上限を設けます。itemsの件数が無制限の場合は、明示的な上限または同時実行数の制限を設けます。N件の並列クエリでも、N個の接続を開くためです。まとめた結合のほうが速いとEXPLAINで確認できた場合を除き、並列実行の形を維持します。バッチ処理は行ごとのインデックスの利点を損なうことが多いためです。 - ページネーションの件数。件数と行は1つのクエリから取得します。
count(*) OVER ()ウィンドウ関数または単一のCTEを使い、データクエリと別にCOUNT(*)を実行しません。 - インデックスの適用範囲
[pg]。本番環境のすべてのWHERE句に、対応するインデックスが必要です。similarity(col, ...)、@@ to_tsvector(...)、またはLIKE '%pattern%'を使う場合は、同じPRに対応するgin (col gin_trgm_ops)またはgin (to_tsvector('lang', col))インデックスを追加するか、逐次スキャンを許容するADRまたはIssueを示す1行コメントを記載します。生のsimilarity() > threshold比較より、インデックスを使用できる%演算子を優先します。ただし、しきい値を一致させる場合に限ります。%はセッションのpg_trgm.similarity_threshold(デフォルトは0.3)と比較するため、クエリのしきい値に設定し、結果セットが同等であることを確認してから、プランナーがインデックスを選択することをEXPLAINで検証します。 - STABLE関数の再利用
[pg]。pg_trgm.similarityとts_rankはIMMUTABLEではなくSTABLEであるため、PostgreSQLは1つのSELECT内での重複呼び出しを確実にはメモ化しません。投影とGREATEST(...)の両方など、同じ値を2回使う場合は、WITH ... AS MATERIALIZEDCTEで1回だけ計算します。通常のサブクエリエイリアスでは、これを保証できません。プランナーがサブクエリをフラット化し、行ごとに評価することがあります。 - 必要な列だけを選択。一覧エンドポイントでは、行全体を選択せず、必要な列を指定します。
- 取得済みの行を再利用。所有権チェックのために取得した行を、ハンドラーが返す行として使います。IDで再度取得しません。
- 書き換える前にEXPLAINを確認
[pg]。クエリの構造変更、バッチ処理、結合、インデックス追加、または並列N件からバッチ1件への置き換えを行う場合は、代表的なデータを使った現行と変更後の形状のEXPLAIN (ANALYZE, BUFFERS)の数値を提示します。速いほうの形状を維持します。差分では速そうに見える書き換えでも、実データでは10〜100倍遅くなることがあります。 - 競合状態。2つ以上の書き込み処理が同じ行にアクセスする可能性がある場合は、トランザクションを使います。非同期処理の完了順は、ソースコード上の順序と同じとは限りません。偶然の逐次化に頼らず、
awaitまたはPromise.allで明示的に順序を制御します。チェックと書き込みを原子的に行う必要がある場合は、アプリケーションレベルのロックではなく、[pg]でのSELECT ... FOR UPDATEまたは一意制約を使います。
コメント
Markdown 対応