Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Eager loading load association for every STI subclass when it is declared in superclass

#125

When association is declared in superclass eager loading of this association produces one query for each subclass. Example:

Update edge Rails and generate empty project:

cd ~/dev/ruby/rails/
git pull
ruby railties/bin/rails ~/dev/test/eager
ln -s ~/dev/ruby/rails ~/dev/test/eager/vendor
cd ~/dev/test/eager/

Generate models:

./script/generate model User name:string
./script/generate model Asset type:string user_id:integer

rake db:migrate

app/models:

class User < ActiveRecord::Base
  has_many :assets
end

class Asset < ActiveRecord::Base
  belongs_to :user
end

class Image < Asset
end

class Audio < Asset
end

class Video < Asset
end

Console session:

Prepare data:

>> User.create
>> User.create
>> Image.create :user => User.first
>> Image.create :user => User.last
>> Audio.create :user => User.first
>> Video.create :user => User.first

Find all Assets with user preloading:

>> Asset.find :all, :include => :user

Produces queries:

  Asset Load (0.002034)   SELECT * FROM "assets"
  User Load (0.001601)   SELECT * FROM "users" WHERE ("users".id IN ('6','5'))
  User Load (0.001147)   SELECT * FROM "users" WHERE ("users".id IN ('5'))
  User Load (0.001194)   SELECT * FROM "users" WHERE ("users".id IN ('5'))

I think when association is declared in superclass it must be only one query to preload it, not one for each subclass. Such as:

  Asset Load (0.002034)   SELECT * FROM "assets"
  User Load (0.001601)   SELECT * FROM "users" WHERE ("users".id IN ('6','5'))

Reported by Denis Barushev · May 6th, 2008 @ 07:10 PM

State: resolved
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. Frederick Cheung
    Frederick Cheung

    Well [http://dev.rubyonrails.org/ticke...][this ticket] is why the current behaviour is what it is. I can't think off the top of my head whether that the ticket and what you've noticed can easily be reconciled.

    Whatever it is, the fix is likely to be in preload_one_association where it does records.group_by(&:class); if it can be determined when this partitoning is not necessary and fall back.

    May 6th, 2008 @ 10:51 PM

  2. Frederick Cheung
    Frederick Cheung

    I believe this does the trick. Would be good for those bitten by this to try it out

    May 7th, 2008 @ 12:16 AM

  3. Frederick Cheung
  4. Denis Barushev
  5. Tarmo Tänav
    Tarmo Tänav

    There seems to be an unused "class_to_reflection = {}" there, planning to cache the reflection for each cache?

    May 7th, 2008 @ 08:23 PM

  6. Frederick Cheung
    Frederick Cheung

    D'oh, somehow manager to produce a patch with not the final version (and mix that in with a patch from another ticket I'd applied locally to test - no idea how that happened!). I think I have untangled my git-tardedness now

    May 7th, 2008 @ 12:30 AM

  7. Denis Barushev
    Denis Barushev
    • Title changed from Eager loading load association for every STI subclass when it is declared in superclass to [PATH] Eager loading load association for every STI subclass when it is declared in superclass

    I'm not good familiar with Rails sources, but I fixed this problem. See path.

    Manual test:

    class Asset < ActiveRecord::Base
      belongs_to :user
    end
    
    class Image < Asset
    end
    
    class Audio < Asset
    end
    
    class Video < Asset
    end
    
    
    class Asset1 < ActiveRecord::Base
      belongs_to :user
    end
    
    class Image1 < Asset1
    end
    
    class Audio1 < Asset1
      belongs_to :user
    end
    
    class Video1 < Asset1
    end
    
    >> Asset.destroy_all
    >> Asset1.destroy_all
    
    >> Audio.create :user => User.first
    >> Audio.create :user => User.last
    >> Video.create :user => User.first
    >> Image.create :user => User.first
    
    >> Audio1.create :user => User.first
    >> Audio1.create :user => User.last
    >> Video1.create :user => User.first
    >> Image1.create :user => User.first
    
    >> Asset.all :include => :user
    >> Asset1.all :include => :user
    

    SQL-queries:

      Asset Load (0.003225)   SELECT * FROM "assets"
      User Load (0.001348)   SELECT * FROM "users" WHERE ("users".id IN ('1','2'))
    
      Asset1 Load (0.001352)   SELECT * FROM "asset1s"
      User Load (0.000919)   SELECT * FROM "users" WHERE ("users".id IN ('1','2'))
      User Load (0.000703)   SELECT * FROM "users" WHERE ("users".id IN ('1'))
    

    How test this behavior in Test::Unit?

    May 7th, 2008 @ 08:22 PM

  8. Frederick Cheung
    Frederick Cheung

    See the patch I attached :-)

    May 7th, 2008 @ 10:51 AM

  9. Denis Barushev
    Denis Barushev
    • Title changed from Eager loading load association for every STI subclass when it is declared in superclass to [PATH] Eager loading load association for every STI subclass when it is declared in superclass

    Oops, I haven't seen your patches. Lighthouse doesn't have intuitive interface.

    Sorry :)

    May 7th, 2008 @ 08:23 PM

  10. Repository
    Repository
    • State changed from new to resolved
    • Title changed from [PATH] Eager loading load association for every STI subclass when it is declared in superclass to Eager loading load association for every STI subclass when it is declared in superclass

    (from [236f0bb67adecbc1e6dac5258e4a8cb310ffd7a4]) When preloading group by reflection rather than by class [#125 state:resolved]

    This avoids extra queries when several subclasses inherit the association

    from their parent class, while still coping with subclasses redefining

    associations.

    Signed-off-by: Pratik Naik

    http://github.com/rails/rails/co...

    May 11th, 2008 @ 10:16 PM