Merge pull request #46041 from Shopify/fix-ar-log-sub

Fix Active Record :db_runtime metric
This commit is contained in:
Jean Boussier 2022-09-15 12:20:45 +02:00 committed by GitHub
commit fc71f585ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 26 deletions

View File

@ -19,7 +19,7 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
end end
def create def create
ActiveRecord::LogSubscriber.runtime += 100 ActiveRecord::RuntimeRegistry.sql_runtime += 100.0
Project.last Project.last
redirect_to "/" redirect_to "/"
end end
@ -32,7 +32,7 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
def db_after_render def db_after_render
render inline: "Hello world" render inline: "Hello world"
Project.all Project.all
ActiveRecord::LogSubscriber.runtime += 100 ActiveRecord::RuntimeRegistry.sql_runtime += 100.0
end end
end end
@ -72,7 +72,7 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
end end
def test_runtime_reset_before_requests def test_runtime_reset_before_requests
ActiveRecord::LogSubscriber.runtime += 12345 ActiveRecord::RuntimeRegistry.sql_runtime += 12345.0
get :zero get :zero
wait wait

View File

@ -7,16 +7,24 @@ module ActiveRecord
class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
def self.runtime=(value) def self.runtime=(value)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
ActiveRecord::LogSubscriber.runtime= is deprecated and will be removed in Rails 7.2.
MSG
ActiveRecord::RuntimeRegistry.sql_runtime = value ActiveRecord::RuntimeRegistry.sql_runtime = value
end end
def self.runtime def self.runtime
ActiveRecord::RuntimeRegistry.sql_runtime ||= 0 ActiveSupport::Deprecation.warn(<<-MSG.squish)
ActiveRecord::LogSubscriber.runtime is deprecated and will be removed in Rails 7.2.
MSG
ActiveRecord::RuntimeRegistry.sql_runtime
end end
def self.reset_runtime def self.reset_runtime
rt, self.runtime = runtime, 0 ActiveSupport::Deprecation.warn(<<-MSG.squish)
rt ActiveRecord::LogSubscriber.reset_runtime is deprecated and will be removed in Rails 7.2.
MSG
ActiveRecord::RuntimeRegistry.reset
end end
def strict_loading_violation(event) def strict_loading_violation(event)
@ -29,8 +37,6 @@ module ActiveRecord
subscribe_log_level :strict_loading_violation, :debug subscribe_log_level :strict_loading_violation, :debug
def sql(event) def sql(event)
self.class.runtime += event.duration
payload = event.payload payload = event.payload
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name]) return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/attr_internal"
require "active_record/log_subscriber" require "active_record/runtime_registry"
module ActiveRecord module ActiveRecord
module Railties # :nodoc: module Railties # :nodoc:
@ -23,16 +23,16 @@ module ActiveRecord
# We also need to reset the runtime before each action # We also need to reset the runtime before each action
# because of queries in middleware or in cases we are streaming # because of queries in middleware or in cases we are streaming
# and it won't be cleaned up by the method below. # and it won't be cleaned up by the method below.
ActiveRecord::LogSubscriber.reset_runtime ActiveRecord::RuntimeRegistry.reset
super super
end end
def cleanup_view_runtime def cleanup_view_runtime
if logger && logger.info? && ActiveRecord::Base.connected? if logger && logger.info? && ActiveRecord::Base.connected?
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime db_rt_before_render = ActiveRecord::RuntimeRegistry.reset
self.db_runtime = (db_runtime || 0) + db_rt_before_render self.db_runtime = (db_runtime || 0) + db_rt_before_render
runtime = super runtime = super
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime db_rt_after_render = ActiveRecord::RuntimeRegistry.reset
self.db_runtime += db_rt_after_render self.db_runtime += db_rt_after_render
runtime - db_rt_after_render runtime - db_rt_after_render
else else
@ -43,7 +43,7 @@ module ActiveRecord
def append_info_to_payload(payload) def append_info_to_payload(payload)
super super
if ActiveRecord::Base.connected? if ActiveRecord::Base.connected?
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.reset
end end
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require "active_record/log_subscriber" require "active_record/runtime_registry"
module ActiveRecord module ActiveRecord
module Railties # :nodoc: module Railties # :nodoc:
@ -9,9 +9,9 @@ module ActiveRecord
def instrument(operation, payload = {}, &block) def instrument(operation, payload = {}, &block)
if operation == :perform && block if operation == :perform && block
super(operation, payload) do super(operation, payload) do
db_runtime_before_perform = ActiveRecord::LogSubscriber.runtime db_runtime_before_perform = ActiveRecord::RuntimeRegistry.sql_runtime
result = block.call result = block.call
payload[:db_runtime] = ActiveRecord::LogSubscriber.runtime - db_runtime_before_perform payload[:db_runtime] = ActiveRecord::RuntimeRegistry.sql_runtime - db_runtime_before_perform
result result
end end
else else

View File

@ -10,11 +10,20 @@ module ActiveRecord
extend self extend self
def sql_runtime def sql_runtime
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
end end
def sql_runtime=(runtime) def sql_runtime=(runtime)
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
end end
def reset
rt, self.sql_runtime = sql_runtime, 0.0
rt
end
end end
end end
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
ActiveRecord::RuntimeRegistry.sql_runtime += (finish - start) * 1_000.0
end

View File

@ -8,21 +8,21 @@ class JobRuntimeTest < ActiveSupport::TestCase
include ActiveRecord::Railties::JobRuntime include ActiveRecord::Railties::JobRuntime
def perform(*) def perform(*)
ActiveRecord::LogSubscriber.runtime += 42 ActiveRecord::RuntimeRegistry.sql_runtime += 42.0
end end
end end
test "job notification payload includes db_runtime" do test "job notification payload includes db_runtime" do
ActiveRecord::LogSubscriber.runtime = 0 ActiveRecord::RuntimeRegistry.sql_runtime = 0.0
assert_equal 42, notification_payload[:db_runtime] assert_equal 42, notification_payload[:db_runtime]
end end
test "db_runtime tracks database runtime for job only" do test "db_runtime tracks database runtime for job only" do
ActiveRecord::LogSubscriber.runtime = 100 ActiveRecord::RuntimeRegistry.sql_runtime = 100.0
assert_equal 42, notification_payload[:db_runtime] assert_equal 42.0, notification_payload[:db_runtime]
assert_equal 142, ActiveRecord::LogSubscriber.runtime assert_equal 142.0, ActiveRecord::RuntimeRegistry.sql_runtime
end end
private private

View File

@ -247,10 +247,6 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_equal 0, @logger.logged(:debug).size assert_equal 0, @logger.logged(:debug).size
end end
def test_initializes_runtime
Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join
end
if ActiveRecord::Base.connection.prepared_statements if ActiveRecord::Base.connection.prepared_statements
def test_where_in_binds_logging_include_attribute_names def test_where_in_binds_logging_include_attribute_names
Developer.where(id: [1, 2, 3, 4, 5]).load Developer.where(id: [1, 2, 3, 4, 5]).load