Improve query performance with includes and add comprehensive performance tests
- Replace left_joins with includes in auto-join queries for better performance - Remove complex after_initialize callback since includes handles association loading - Add automatic includes to find method for consistent behavior - Add comprehensive performance tests with query counting - Fix Sorbet type checking by adding ActiveSupport::Notifications to todo.rbi Performance improvements: - where() queries now use single query with includes - N+1 queries avoided for multiple record loading - All query methods (find, find_by, where) now optimized
This commit is contained in:
@@ -61,20 +61,9 @@ module ActiveRecord
|
||||
private
|
||||
|
||||
def setup_auto_loading(association_name)
|
||||
# Use after_initialize to eagerly load aux data
|
||||
self.after_initialize do
|
||||
# Only for persisted records to avoid unnecessary queries
|
||||
if persisted?
|
||||
# Trigger the association load if not already loaded
|
||||
association_obj = association(association_name)
|
||||
unless association_obj.loaded?
|
||||
# Load the aux record (this will create one if it doesn't exist)
|
||||
send(association_name)
|
||||
# Mark as loaded to prevent future queries
|
||||
association_obj.loaded!
|
||||
end
|
||||
end
|
||||
end
|
||||
# Auto-loading is now handled via includes in query methods
|
||||
# No need for after_initialize callback since we use includes
|
||||
# which properly loads associations
|
||||
end
|
||||
|
||||
def setup_query_extensions(association_name)
|
||||
@@ -84,6 +73,7 @@ module ActiveRecord
|
||||
# Store original methods
|
||||
original_where = model_class.method(:where)
|
||||
original_find_by = model_class.method(:find_by)
|
||||
original_find = model_class.method(:find)
|
||||
|
||||
# Define a helper method for processing where conditions
|
||||
model_class.define_singleton_method(:process_aux_where) do |*args|
|
||||
@@ -94,11 +84,11 @@ module ActiveRecord
|
||||
|
||||
# Check if any aux columns are referenced
|
||||
if conditions.keys.any? { |key| aux_columns.include?(key.to_s) }
|
||||
# Split conditions and build query with join
|
||||
# Split conditions and build query with includes
|
||||
main_conditions, aux_conditions =
|
||||
split_conditions(conditions, aux_columns)
|
||||
|
||||
relation = self.left_joins(association_name)
|
||||
relation = self.includes(association_name)
|
||||
relation = relation.where(main_conditions) if main_conditions.any?
|
||||
relation =
|
||||
relation.where(
|
||||
@@ -127,16 +117,27 @@ module ActiveRecord
|
||||
aux_columns = get_aux_column_names
|
||||
|
||||
if conditions.keys.any? { |key| aux_columns.include?(key.to_s) }
|
||||
# Use the enhanced where method and get first result
|
||||
self.where(conditions).first
|
||||
# Use the enhanced where method with includes and get first result
|
||||
self.includes(association_name).where(conditions).first
|
||||
else
|
||||
original_find_by.call(*args)
|
||||
# Use includes for non-aux queries to preload auxiliary data
|
||||
self.includes(association_name).find_by(*args)
|
||||
end
|
||||
else
|
||||
original_find_by.call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
model_class.define_singleton_method(:find) do |*args|
|
||||
# Override find to automatically include aux table joins
|
||||
if has_aux_tables?
|
||||
# Use includes to eager-load auxiliary data in the same query
|
||||
self.includes(association_name).find(*args)
|
||||
else
|
||||
original_find.call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
# Also override where on ActiveRecord::Relation for chained calls
|
||||
setup_relation_where_override(association_name)
|
||||
end
|
||||
@@ -154,16 +155,16 @@ module ActiveRecord
|
||||
|
||||
# Check if any aux columns are referenced
|
||||
if conditions.keys.any? { |key| aux_columns.include?(key.to_s) }
|
||||
# Split conditions and ensure aux join exists
|
||||
# Split conditions and ensure aux includes exists
|
||||
main_conditions, aux_conditions =
|
||||
klass.split_conditions(conditions, aux_columns)
|
||||
|
||||
# Ensure we have the aux join
|
||||
# Ensure we have the aux includes
|
||||
relation = self
|
||||
unless relation.joins_values.any? { |join|
|
||||
join.to_s.include?(association_name.to_s)
|
||||
unless relation.includes_values.any? { |include_val|
|
||||
include_val == association_name
|
||||
}
|
||||
relation = relation.left_joins(association_name)
|
||||
relation = relation.includes(association_name)
|
||||
end
|
||||
|
||||
# Apply conditions
|
||||
|
||||
Reference in New Issue
Block a user