数行だけのストアドプロシージャ改修が、インデックスを使わなくなる性能劣化を引き起こし本番障害になった——ZOZO TECH BLOGが公開した事例の起点はここにある。原因は改修量ではなく、「軽微かどうか」を性能への影響を測る前に人間が判断していた構造そのものだった。対策として組まれたのが、STG環境で実行計画を機械的に取得し、その構造化データをLLMに一次ソースとして読ませるCIパイプラインだ。
ZOZO TECH BLOGの事例から、ログではなく実行計画XMLという構造化データをLLMに直接読ませる設計と、「ルールベースの機械判定→閾値超えだけをLLMに読ませる」という3段構えの検証フローを取り出し、個人開発のDB改修やパフォーマンス確認にも応用できる形に翻訳したもの。
きっかけは本番障害だった。ストアドプロシージャの改修でWHERE句が参照するカラムが変わり、それまで効いていたインデックスが使われなくなった。事後に見れば典型的な性能劣化だが、問題は改修量が数行程度の小さなものだったことにある。「軽微な変更だから」という理由で、リリース前後の性能検証というプロセスそのものが素通りされていた。
ここでの本質的な課題は、コードレビューの精度不足ではない。「この変更が軽微かどうか」を、性能への影響を実際に測る前に人間の感覚で判断していたという構造の方だ。小さな差分ほど「大丈夫だろう」という直感が働きやすく、しかもその直感を裏付けるデータが存在しない。だからこの仕組みは、人間の判断を置き換えるのではなく、「軽微かどうかの判断材料」を機械的に先に作ることを狙っている。
パイプラインはGitHub ActionsとArgo Workflows(Amazon EKS上で稼働)の2つで役割を分担し、両者のデータの受け渡しはすべてAmazon S3を経由する構成になっている。
PRの変更検出、ルールベース解析、レビュー結果のPRコメント生成を担当する。DBには直接触れない。
DBへの接続はこちらに一任されている。STG環境でのストアド作成・実行計画の取得というリスクのある操作を、GitHub Actions側から切り離している。接続はOIDCによるIAM Roleの引き受けで行われる。
実行計画XMLを含む大容量ファイルの受け渡しに使う。GitHub Actionsのペイロードサイズ制限を回避する実務的な選択。
最終段の性能懸念の判定を担う。モデル呼び出しにはApplication Inference Profileを使い、コスト配分タグを付けて実行コストを追跡できるようにしている。実装はclaude-code-actionを使用。
「DBに触れる操作」と「解析・判定」を別ランタイムに分離している点が構成上の要点で、GitHub Actions単体で完結させていない理由もここにある。
この仕組みの安全性は「本番DBへ一切接続せず、STG環境で推定の実行計画を取得する」という一点に支えられている。STG環境は本番規模のデータ量を保っており、精度を保ちながらも本番への影響ゼロを両立させている。
STGにストアドを作成する際は_CI_PR<Pull Request番号>_add|before|after_<ストアド名>という命名規則で、変更前後・追加の3種を衝突なく区別する一時名を割り当てる。SQL Serverの識別子上限は128文字のため、超過時は元名を切り詰めたうえで8桁ハッシュを付与し一意化する。安全策も二重に組まれていて、trapでエラー時にも必ずDROP PROCEDUREが実行されるようにし、さらにArgo Workflows側でも渡されたSQLにCI用の名前が含まれているかを検証、既存のストアド名でCREATEしようとした場合はチェックそのものを失敗させる。
実行計画の取得はSET SHOWPLAN_XML ONを使う。これを有効にすると、以降のステートメントはコンパイルだけが行われ実際には実行されない。ストアドをEXECしてもクエリ自体は走らないため、引数なしのEXECだけで実行計画XMLが返る。パラメータを渡さずに済むのが実務上の利点だが、その代わりオプティマイザは統計情報のヒストグラムではなく密度ベクターによる平均値から行数を見積もるため、実データでの実行計画とは精度が異なりうる点は割り切りとして残る。
LLMにいきなり「性能劣化していないか判定して」と丸投げしていない点がこの設計の芯になる。
Missing Index(Impactが50%以上ならhigh判定)、Table Scan / Clustered Index ScanなどのScan・Lookup系演算子、推定サブツリーコストが10以上のSort / Hash Matchなど高コスト演算子、暗黙の型変換や結合述語なしといったWarningsを機械的に抽出する。
総推定コストの増加率が20%以上、または変更後にのみ新規で出現した懸念があれば「悪化」と判定する。ここまでは決定的なルールで完結し、LLMを呼ばない。
claude-code-action経由でClaudeを呼び出し、実行計画XMLを一次ソースとして優先的に読ませるプロンプトを組んでいる。ログの要約ではなく、構造化データそのものを読ませてblock / passのverdictを返させる。
ここが実務Tipsの核心。LLMに読ませているのは人間向けの説明ログではなく、SQL ServerがそのままXMLで吐く実行計画という一次データ。ルールベースで機械的に判定できる範囲は先に閉じてしまい、閾値を超えた"人間の判断が要る領域"だけをLLMに渡している。
この記事で誠実だと感じる点は、完成した仕組みとして誇張していないことだ。「チェック結果をマージの可否へ接続する仕組み」自体は説明されているが、「現時点では、この章で説明する設定をまだ有効にしていません」と明記されている。テスト運用として、結果をPull Requestへコメントするだけの状態にとどめ、誤検知の傾向を見ている段階だという。
なお、ルールベース解析やLLMレビューを経ても、DBRE(Database Reliability Engineering)チームによるレビュー済みを示すdbre-review-passedラベルが付いている場合は後続の自動チェックをスキップする救済ルールも用意されている。今後はルールベース解析の閾値とLLMレビューのプロンプトの判定条件を調整しながら、順次適用を進めていく予定とされている。
個人開発でここまで大掛かりなパイプラインを組む必要はないが、設計そのものは縮小版として持ち込める。DB改修やクエリのパフォーマンス確認をするとき、EXPLAINやクエリプランの出力を毎回自分で読み比べるのは面倒で、結局「軽微だから」と省略しがちなのはこの事例の本番障害と同じ構造だ。
応用できる型は3つに絞れる。ログではなく構造化データを一次ソースにする——要約された文章より、実行計画やdiffのような構造化データをそのままLLMに読ませた方が判断がぶれない。機械的に足切りできる範囲は先に閉じる——コスト増加率のような数値の閾値判定は、LLMを呼ぶ前の決定的なルールで済ませる。本番に触れない環境で完結させる——検証のためだけにリスクのある操作をする場所を、本番から明確に切り離す。この3つは、DB改修に限らずビルド時間やAPIレスポンスタイムの比較など、「変更前後を比べて閾値超えだけ精査する」場面全般に転用できる。
マージブロックをまだ有効化していないという正直な進捗開示も含めて、「仕組みを作った」で終わらせず「まだ誤検知を見ている段階」と言い切れる運用は、そのまま検品文化の実例としても読める。