ActiveRecord で発行されるクエリにコメントを記載したい
こんにちは、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 を使う)
データベースからスロークエリログが出力されたときに、どこの処理で使われたクエリなのかを知りたい場合に使えます。
次のように設定を有効にすると、クエリにログが出力されるようになります。
# 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」が自動的に無効になりますのでご注意ください。
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からどこの処理で呼び出されたかを確認できます。
Discussion