Merge pull request #18846 from hundredwatt/feat/warn-on-result-set-size
Add `config.active_record.warn_on_result_set_size` option
This commit is contained in:
commit
8b451e3a31
@ -1,3 +1,10 @@
|
||||
* Add `config.active_record.warn_on_records_fetched_greater_than` option
|
||||
|
||||
When set to an integer, a warning will be logged whenever a result set
|
||||
larger than the specified size is returned by a query. Fixes #16463
|
||||
|
||||
*Jason Nochlin*
|
||||
|
||||
* Ignore psqlrc when loading database structure.
|
||||
|
||||
*Jason Weathered*
|
||||
|
@ -94,6 +94,15 @@ def self.configurations
|
||||
mattr_accessor :dump_schemas, instance_writer: false
|
||||
self.dump_schemas = :schema_search_path
|
||||
|
||||
##
|
||||
# :singleton-method:
|
||||
# Specify a threshold for the size of query result sets. If the
|
||||
# number of records in the set exceeds threshold, a warning is
|
||||
# logged. This should be used to identify queries which pull
|
||||
# thousands of records, which may cause memory bloat.
|
||||
mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
|
||||
self.warn_on_records_fetched_greater_than = nil
|
||||
|
||||
mattr_accessor :maintain_test_schema, instance_accessor: false
|
||||
|
||||
mattr_accessor :belongs_to_required_by_default, instance_accessor: false
|
||||
|
@ -102,6 +102,14 @@ class Railtie < Rails::Railtie # :nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
initializer "active_record.warn_on_records_fetched_greater_than" do
|
||||
if config.active_record.warn_on_records_fetched_greater_than
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
require 'active_record/relation/record_fetch_warning'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initializer "active_record.set_configs" do |app|
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
app.config.active_record.each do |k,v|
|
||||
|
@ -0,0 +1,50 @@
|
||||
module ActiveRecord
|
||||
class Relation
|
||||
module RecordFetchWarning
|
||||
# When this module is prepended to ActiveRecord::Relation and
|
||||
# `config.active_record.warn_on_records_fetched_greater_than` is
|
||||
# set to an integer, if the number of records a query returns is
|
||||
# greater than the value of `warn_on_records_fetched_greater_than`,
|
||||
# a warning is logged. This allows for the dection of queries that
|
||||
# return a large number of records, which could cause memory
|
||||
# bloat.
|
||||
#
|
||||
# In most cases, fetching large number of records can be performed
|
||||
# efficiently using the ActiveRecord::Batches methods.
|
||||
# See active_record/lib/relation/batches.rb for more information.
|
||||
def exec_queries
|
||||
QueryRegistry.reset
|
||||
|
||||
super.tap do
|
||||
if logger && warn_on_records_fetched_greater_than
|
||||
if @records.length > warn_on_records_fetched_greater_than
|
||||
logger.warn "Query fetched #{@records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
||||
payload = args.last
|
||||
|
||||
QueryRegistry.queries << payload[:sql]
|
||||
end
|
||||
|
||||
class QueryRegistry # :nodoc:
|
||||
extend ActiveSupport::PerThreadRegistry
|
||||
|
||||
attr_accessor :queries
|
||||
|
||||
def initialize
|
||||
reset
|
||||
end
|
||||
|
||||
def reset
|
||||
@queries = []
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Relation.prepend ActiveRecord::Relation::RecordFetchWarning
|
@ -0,0 +1,28 @@
|
||||
require 'cases/helper'
|
||||
require 'models/post'
|
||||
|
||||
module ActiveRecord
|
||||
class RecordFetchWarningTest < ActiveRecord::TestCase
|
||||
fixtures :posts
|
||||
|
||||
def test_warn_on_records_fetched_greater_than
|
||||
original_logger = ActiveRecord::Base.logger
|
||||
orginal_warn_on_records_fetched_greater_than = ActiveRecord::Base.warn_on_records_fetched_greater_than
|
||||
|
||||
log = StringIO.new
|
||||
ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
|
||||
ActiveRecord::Base.logger.level = Logger::WARN
|
||||
|
||||
require 'active_record/relation/record_fetch_warning'
|
||||
|
||||
ActiveRecord::Base.warn_on_records_fetched_greater_than = 1
|
||||
|
||||
Post.all.to_a
|
||||
|
||||
assert_match(/Query fetched/, log.string)
|
||||
ensure
|
||||
ActiveRecord::Base.logger = original_logger
|
||||
ActiveRecord::Base.warn_on_records_fetched_greater_than = orginal_warn_on_records_fetched_greater_than
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user