SQL通知を接続単位で無効化できる `sql_notifications` オプションを追加

rails/rails

Active Record の SQL 通知をデータベース接続ごとに無効化できる sql_notifications オプションが追加され、不要な計測オーバーヘッドを抑制できるようになりました。設定を false にすれば、対象接続からは sql.active_record イベントが一切発行されません。

背景

Solid Cache や Solid Queue など、特定のデータベース接続で Active Record の計測が不要になるケースが増えていました。従来は内部の instrumenter を置き換えるパッチを当て、すべての呼び出し元で disable_instrumentation ラップを行うという、侵入度の高い手法が取られていました。

その手法は内部実装に依存し、コードベース全体に散らばるラップ処理が保守コストを上げる原因となっていました。接続単位で簡潔に無効化できる設定が求められ、今回の PR で実装されました。

この背景から、設定だけで通知抑制が可能になる という点が本変更の主旨です。

技術的な変更

activerecord/lib/active_record/connection_adapters/abstract_adapter.rbsql_notifications 設定項目が追加され、デフォルトは true です。`

@@
-        @config.fetch(:advisory_locks, true)
+        @config.fetch(:advisory_locks, true)
+      )
+
+        @sql_notifications = self.class.type_cast_config_to_boolean(
+          @config.fetch(:sql_notifications, true)
+        )

この設定は sql_notifications? メソッドで外部から参照でき、接続インスタンスが通知を行うか判定します。

@@
-      def prepared_statements_disabled_cache # :nodoc:
+      def sql_notifications?
+        @sql_notifications
+      end

ActiveRecord::ConnectionAdapters::AbstractAdapter#instrumenter は、sql_notifications?false の場合に ActiveSupport::Notifications.null_instrumenter を返すよう変更されました。

@@
-          ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
+          if sql_notifications?
+            ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
+          else
+            ActiveSupport::Notifications.null_instrumenter
+          end

非同期クエリ用の EventBuffer も同様に sql_notifications? が真の場合にだけ生成されます。これにより、通知が無効化された接続ではイベントバッファも不要となります。

@@
-                @event_buffer = EventBuffer.new(self, ActiveSupport::Notifications.instrumenter)
-                ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer
+                if connection.sql_notifications?
+                  @event_buffer = EventBuffer.new(self, ActiveSupport::Notifications.instrumenter)
+                  ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer
+                end

activesupport 側にも NullInstrumenter クラスが新規追加され、ActiveSupport::Notifications.null_instrumenter がこのシングルトンを返します。instrument はブロックをそのまま実行し、通知は一切発行しません。

@@
+    class NullInstrumenter
+      class NullHandle; def start; end; def finish; end; end
+      NULL_HANDLE = NullHandle.new.freeze
+      def instrument(name, payload = {}); yield payload if block_given?; end
+      def build_handle(name, payload); NULL_HANDLE; end
+      def start(name, payload); end; def finish(name, payload); end
+    end

テストは activerecord/test/cases/adapter_test.rb に追加され、sql_notifications: false の接続で sql.active_record 通知が空であること、デフォルトは通知が出ることを検証しています。

これらの変更は デフォルト動作を維持しつつ、オプションだけで通知抑制を可能にする という最小侵入の設計です。

設計判断

設定キーは既存の sql_notifications を拡張し、新規キーを導入しない方針が採られました。デフォルトは true で、既存アプリへの影響はありません。これにより後方互換性が確保されています。

NullInstrumenter はステートレスかつスレッドセーフな単一インスタンスとして実装され、Per‑thread の instrumenter レジストリを回避 できる点がメリットです。通知無効化のために余計なオブジェクトを生成しない設計は、リソース節約にもつながります。

非同期クエリのバッファリング処理も sql_notifications? が偽の場合はスキップされます。イベントバッファ自体が不要になることで、メモリ使用量と CPU 負荷が低減されることが期待できます。

以上の判断は、「機能追加は最小限のコード変更で、設定だけで有効/無効を切り替えられる」 という Rails の設計哲学に沿ったものです。

まとめ

sql_notifications 接続オプションにより、データベース接続ごとに SQL 通知の有無を制御できるようになり、Solid Cache や Solid Queue などの内部計測が不要なケースでオーバーヘッドを除去できます。NullInstrumenter によるノーオペレーション実装と、デフォルト互換性を保つ設計が本変更の核心です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
59420a2e

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的な変更、まとめがすべて揃っており、3部構成が明確です。設計判断は任意要素として適切に配置されています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックのファイル名付きハイライトは正しい形式です。ただし PR リンクが "[PR #56899]" となっており、指定の "[#123](URL)" 形式になっていません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

内容は Rails の内部実装に詳しいエンジニア向けで、初心者向けの過度な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論の構成が保たれ、段落はトピックセンテンスで始まり、1段落1トピック、長さも適切です。空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供された Diff と一致しており、追加・変更箇所が正しく引用されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`sql_notifications`、`NullInstrumenter`、`instrumenter` などの用語は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明は Diff と PR の内容に基づいており、誤った主張や論理的矛盾は見られません。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全ての事実は PR の背景・目的・実装と照合でき、捏造や推測はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #56899 だけが使用されており、正確です。その他数値や固有名詞の誤りはありません。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトルの意味を日本語で正確に伝えており、一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

Rails の設計哲学に関する一般的記述は PR に含まれる概念と矛盾せず、外部情報の捏造はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PR の記述と整合しています。