[wip] loading optimizations

This commit is contained in:
Dylan Knutson
2025-07-28 00:22:57 +00:00
parent 2090564947
commit ba1b74022a
7 changed files with 189 additions and 47 deletions

View File

@@ -234,13 +234,13 @@ RSpec.describe HasAuxTable do
it "allows saving the model with auxiliary columns" do
car = Car.create!(name: "Honda Civic")
num_queries =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car.fuel_type = "hybrid"
car.engine_size = 1.8
car.save!
end
expect(num_queries).to eq(1)
expect(queries.length).to eq(1)
end
end
@@ -423,32 +423,32 @@ RSpec.describe HasAuxTable do
# All query methods now use single queries with proper LEFT OUTER JOINs
it "loads single model with auxiliary data in one query using find" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car = Car.find(@car1.id)
# Access auxiliary attributes to ensure they're loaded
car.fuel_type
car.engine_size
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "loads single model with auxiliary data in one query using find_by" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car = Car.find_by(name: "Toyota Prius")
# Access auxiliary attributes to ensure they're loaded
car.fuel_type
car.engine_size
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "loads multiple models with auxiliary data in one query using where" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars = Car.where(fuel_type: %w[hybrid electric])
# Access auxiliary attributes for all cars
cars.each do |car|
@@ -457,7 +457,7 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "avoids N+1 queries when loading multiple models" do
@@ -472,8 +472,8 @@ RSpec.describe HasAuxTable do
end
cars = nil
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars = Car.where(fuel_type: "gasoline")
# Access auxiliary attributes for all cars - should not trigger additional queries
cars.each do |car|
@@ -483,13 +483,13 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1) # Single query regardless of how many cars are loaded
expect(queries.length).to eq(1) # Single query regardless of how many cars are loaded
expect(cars.length).to be >= 1 # At least the original Honda Civic plus new cars
end
it "uses single query when ordering by auxiliary columns" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars = Car.where(engine_size: 1.0..3.0).order(:engine_size)
# Access all attributes
cars.each do |car|
@@ -499,12 +499,12 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "uses single query for complex auxiliary column queries" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars =
Car.where(fuel_type: "hybrid").or(Car.where(engine_size: 0.0))
# Access all attributes
@@ -515,12 +515,12 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "uses single query when finding by auxiliary columns" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car = Car.find_by(fuel_type: "hybrid", name: "Toyota Prius")
# Access all attributes
car.name
@@ -528,7 +528,7 @@ RSpec.describe HasAuxTable do
car.engine_size
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "doesn't trigger additional queries when accessing auxiliary attributes after load" do
@@ -536,8 +536,8 @@ RSpec.describe HasAuxTable do
car = Car.find(@car1.id)
# Now count queries when accessing auxiliary attributes
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car.fuel_type
car.engine_size
car.fuel_type? # presence check
@@ -545,12 +545,12 @@ RSpec.describe HasAuxTable do
end
# Currently this should be 0 since auxiliary record is already loaded
expect(query_count).to eq(0) # No additional queries should be triggered
expect(queries.length).to eq(0) # No additional queries should be triggered
end
it "handles mixed queries with main and auxiliary columns in single query" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars = Car.where(name: "Toyota Prius", fuel_type: "hybrid")
cars.each do |car|
car.name
@@ -559,12 +559,12 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "uses single query for range queries on auxiliary columns" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
cars = Car.where(engine_size: 0.0..1.9)
cars.each do |car|
car.name
@@ -573,19 +573,19 @@ RSpec.describe HasAuxTable do
end
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
it "maintains single query performance with limit and offset" do
query_count =
SpecHelper.count_queries do
queries =
SpecHelper.capture_queries do
car = Car.where(fuel_type: %w[hybrid electric]).limit(1).first
# Access auxiliary attributes
car.fuel_type
car.engine_size
end
expect(query_count).to eq(1)
expect(queries.length).to eq(1)
end
end
@@ -983,8 +983,8 @@ RSpec.describe HasAuxTable do
end
it "reloads with one query" do
num_queries = SpecHelper.count_queries { @car.reload }
expect(num_queries).to eq(1)
queries = SpecHelper.capture_queries { @car.reload }
expect(queries.length).to eq(1)
end
it "reloads associations" do
@@ -1027,12 +1027,16 @@ RSpec.describe HasAuxTable do
expect(Car.count).to eq(1)
expect(Boat.count).to eq(1)
expect(SpecHelper.count_queries { car = Vehicle.find(car.id) }).to eq(1)
expect(
SpecHelper.capture_queries { car = Vehicle.find(car.id) }.length
).to eq(1)
expect(car.fuel_type).to eq("gasoline")
expect(car.engine_size).to eq(2.0)
expect(car.name).to eq("Honda Civic")
expect(SpecHelper.count_queries { boat = Vehicle.find(boat.id) }).to eq(1)
expect(
SpecHelper.capture_queries { boat = Vehicle.find(boat.id) }.length
).to eq(1)
expect(boat.only_freshwater).to eq(true)
end

View File

@@ -0,0 +1,36 @@
# typed: false
# frozen_string_literal: true
require "spec_helper"
RSpec.describe "loading optimizations" do
context "cars table" do
before do
Car.create!(name: "Toyota Camry", fuel_type: "gasoline")
Car.create!(name: "Toyota Prius", fuel_type: "hybrid")
Car.create!(name: "Toyota Corolla", fuel_type: "electric")
end
it "queries only the aux table if plucking values that are on the aux table" do
queries =
SpecHelper.capture_queries do
expect(Car.pluck(:fuel_type)).to eq(%w[gasoline hybrid electric])
end
expect(queries.length).to eq(1)
expect(queries.first).not_to include("JOIN")
end
it "queries both tables if main table column is referenced" do
queries =
SpecHelper.capture_queries do
rel = Car.where(name: "Toyota Camry")
rel = rel.pluck(:fuel_type)
expect(rel).to eq(%w[gasoline])
end
expect(queries.length).to eq(1)
expect(queries.first).to include("JOIN")
end
end
end

View File

@@ -62,11 +62,13 @@ module SpecHelper
LOG_QUERIES = T.let(false, T::Boolean)
# Helper method to count queries
sig { params(block: T.proc.void).returns(Integer) }
def self.count_queries(&block)
query_count = 0
sig { params(block: T.proc.void).returns(T::Array[String]) }
def self.capture_queries(&block)
queries = T.let([], T::Array[String])
query_callback =
lambda { |name, start, finish, message_id, values| query_count += 1 }
lambda do |name, start, finish, message_id, values|
queries << values[:sql]
end
ActiveSupport::Notifications.subscribed(
query_callback,
@@ -81,6 +83,6 @@ module SpecHelper
ActiveRecord::Base.logger = old_logger if LOG_QUERIES
end
query_count
queries
end
end