✏️

ActiveRecord で発行されるクエリにコメントを記載したい

2024/06/07に公開

こんにちは、okumudです。
弊社のプロダクトでは MySQL を使用しており、 スロークエリーログ(遅いSQL)の監視をしています。
この記事では、スロークエリの監視から特定のクエリを除外する方法と、スロークエリログの出所を特定する方法について説明します。

執筆時の Ruby on Rails のバージョンは 7.1.3.3 です。

スロークエリの監視から特定のクエリを除外する (annotate を使う)

スロークエリの発生がわかっているとき、監視から外したい場合に使います。
対象のクエリ(SELECTのみ)に対して annotate を呼び出すことで、コメントを挿入することが可能です。
ただし、INSERT や UPDATE、DELETE ではクエリのコメントは出力されません。

User.where(enabled: true).order(:created_at).annotate('ignore_slow_query')
# => SELECT `users`.* FROM `users` WHERE `users`.`enabled` = 1
#    /* ignore_slow_query */ ORDER BY `users`.`created_at` ASC

監視側で特定の文字列(例: ignore_slow_query)が含まれている時は、対象から外す設定を行います。

スロークエリログの出所を特定する (query_log_tags を使う)

データベースからスロークエリログが出力されたときに、どこの処理で使われたクエリなのかを知りたい場合に使えます。

https://railsguides.jp/configuring.html#config-active-record-query-log-tags-enabled

次のように設定を有効にすると、クエリにログが出力されるようになります。

# config/application.rb
module MyApplication
  class Application < Rails::Application
        # 中略
    config.active_record.query_log_tags_enabled = true
    config.active_record.query_log_tags = %i[namespaced_controller action job] # オプション
  end
end

出力例は以下の通りです。

Controller (request) の場合:

SELECT `users`.* FROM `users` LIMIT 10 /*action='show',namespaced_controller='manager%2Fusers'*/

Job の場合:

SELECT `users`.* FROM `users` LIMIT 10 /*job='Manager::UserUpdateJob'*/

📝 ActiveRecord::QueryLogs.prepend_comment = true と設定することで、 コメントを先頭へ挿入することも可能です。

query_log_tags を有効にするときの注意点

Railsガイドにもありますが、「prepared statement」が自動的に無効になりますのでご注意ください。

https://github.com/rails/rails/blob/v7.1.3.3/activerecord/lib/active_record/railtie.rb#L410-L419

prepared statement を有効にしている場合、パフォーマンスへの影響を考慮する必要があります。繰り返しの挿入や更新が遅くなる可能性があり、SQLインジェクションのリスクも増加するためです。
mysql2 アダプタを使用している場合、デフォルトで prepared statement が無効なので影響は少ないと思われますが、導入前に database.yml を確認してください。他のアダプタを使用している場合はデフォルトで prepared statement が有効なのでご注意ください。

query_log_tags へ設定できる項目

config.active_record.query_log_tags に設定できる内容とその出力内容を記載します。

共通 🔗github

:application  # 例: MyApplication
:pid          # 例: 1234
:socket       # データベースのsocket
:db_host      # データベースのホスト名
:database     # データベース名

action_controller を使用している場合 🔗github

:controller            # (デフォルト) 例: users
:action                # (デフォルト) 例: index
:namespaced_controller #                     Rails 7.0以前 例: Manager::UsersController
                       #                     Rails 7.1以降 例: manager/users

📝 :action:controller または :namespaced_controller は必ず設定されます

active_job を使用している場合 🔗github

:job                   # (デフォルト) 例: Manager::UserUpdateJob

📝 :job は必ず設定されます

カスタマイズ

次のように独自のコードを埋め込むことも可能です。

config.active_record.query_log_tags = [
  :namespaced_controller,
  :action,
  :job,
  {
    trace_id: -> { Datadog::Tracing.correlation.trace_id }
  }
]

query_log_tags_format の設定

config.active_record.query_log_tags_format で出力形式を変更可能です。

config.active_record.query_log_tags_format = :legacy
# => /*namespaced_controller:manager/users,action:index*/
config.active_record.query_log_tags_format = :sqlcommenter
# => /*action='index',namespaced_controller='manager%2Fusers'*/

まとめ

annotate メソッドを使うことで特定のクエリに対してコメントを入れることができます。query_log_tags を有効にすることで、スロークエリログなどのSQLからどこの処理で呼び出されたかを確認できます。

SocialPLUS Tech Blog

Discussion