CIで追加されたSQLクエリを自動で検知して、パフォーマンスの問題を未然に防ぐ

解決したかった課題と開発したもの

WEBサービス開発を行っていると、ORMが生成したSQLクエリや、またはコード内に新規に追加したSQLクエリが意図しない、サービスのパフォーマンス劣化を起こしてしまうことがあります。その課題を解決すべく、それらのSQLクエリを自動テスト時に検知し、事前にExplain句などを利用してパフォーマンス計測をしたいと思い、Actionを作成しました。このActionを利用すると、下記のようにPRのコメントで新規クエリを開発者、レビューワーにお知らせします。こちらのPR が実際に動いた際の動作です。

利用方法

自動テストの実行前にMySQLのクエリログをテーブルに出力するようにします。この変更を行う専用のActionがあります。

- uses: pyama86/welcome-new-query/enable-querylog
  with:
    db_host: db

そして、テスト完了後に、クエリログのWhere句などを整形し、重複排除した結果と、前回のデフォルトブランチマージ時点の結果と差分を見ることで、該当のPRのコードで追加されているクエリを検知します。下記のActionをを利用します。

- uses: pyama86/welcome-new-query/analysis
  with:
    save_path: ./new-queries
    db_host: db
    aws_access_key_id : ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    s3_bucket: welcome-newquery

上記の例では突き合わせをするデフォルトブランチの結果はS3に保存しています。AWSを利用したくない場合はcacheアクションを利用して、こちらの例のように保存することもできます。

結果については steps.analysis.outputs.new_queries に保存されているので、後続の処理で、GibHub Issueにコメントしています。

- uses: actions/github-script@v4
  if: ${{ github.event_name == 'pull_request' }}
  # for escape js syntax
  env:
    NEW_QUERIES: "${{ steps.analysis.outputs.new_queries }}"
  with:
    script: |
      const opts = github.issues.listComments.endpoint.merge({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        per_page: 100,
      })

      const comments = await github.paginate(opts)
      for(const comment of comments) {
        if (comment.user.login === "github-actions[bot]" && comment.body.startsWith("#### New Queries found!")) {
          return
        }
      }

      const output = `#### New Queries found! 📖
      <details><summary>Show Queries</summary>

      \`\`\`sql
      ${process.env.NEW_QUERIES.replace(/\\n/g, '\n')}
      \`\`\`

      </details>
      `;

      if(process.env.NEW_QUERIES) {
        github.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: output,
        })
      }

最後に

ポイントは検知されたクエリをPR作成者だけでなくレビューワーも参照することができるので事前にパフォーマンスの問題についてもレビュー可能になることです。ぜひExplainのお供にご利用ください。