Living on the edge (of Rails) #5 – better eager loading and more

In: Edge Rails|Open Source|Ruby|Ruby on Rails

30 Jan 2008

Another week of edge Rails changes, featured on the Rails Envy podcast. This weeks’ report covers changes from 21 Jan 2008 to 27 Jan 2008 (the day the Rails Envy podcast was recorded).

Eager loading :includes does pre-loading

The current gem Rails behavior when loading associations with something like

Author.find(:all, :include => [:posts, :comments])

is to make a big query with multiple joins that fetches all the associations in the same query (for you RDBMS geeks, the “cartesian product”).

Frederick Cheung (fcheung on the Rails Trac) has contributed an impressive optimization to this by pre-loading the associations rather than trying to eager loading with one big query. So a find like this:

Author.find(:all, :include => [:posts, :comments])

would first load all the authors, then all the posts, and then all the comments in 3 smaller, faster queries.

The main benefit of this (depending on your data): Running X simple queries (to fetch authors, then posts and comments) rather than one mega-query that joins all the associated tables is faster most of the time. The result set is often smaller as well.

More details can be found at ticket #9640 on the Rails Trac.

Related changeset: http://dev.rubyonrails.org/changeset/8672

composed_of aggregations can now be used in finder conditions

You can now use value objects that you’ve previously specified that your model is composed_of in the finder conditions hash. E.g. if you have a Customer that has a balance aggregation:

class Customer < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
end

You can now pass a Money value object to Customer#find:

Customer.find(:all, :conditions => { :balance => Money.new(20, "USD") })

Convenient.

Related changeset: http://dev.rubyonrails.org/changeset/8671

New helper: label_tag

A label_tag helper has been missing for quite awhile since the label helper was added. No longer:

label_tag 'name'
# => <label for="name">Name</label>

label_tag 'name', 'Your name'
# => <label for="name">Your name</label>

Related changeset: http://dev.rubyonrails.org/changeset/8685

New ActiveSupport class: ActiveSupport::TimeWithZone

A new ActiveSupport::TimeWithZone class has been introduced to make timezone support in Rails easier. Ryan Daigle has a good writeup on this.

A word of warning though: this is still work in progress, as Geoff Buesing, the core team member responsible for these timezone changes, has more to add on how his plans for the timezone puzzle in the comments.

Related changeset: http://dev.rubyonrails.org/changeset/8696

map.root can be easily aliased

map.root now accept a single symbol as an argument to declare an alias. Just a little something to keep your routes a little more DRY, e.g.:

map.new_session :controller => 'sessions', :action => 'new' 
map.root :new_session

Related changeset: http://dev.rubyonrails.org/changeset/8738

As usual, let me know of any inaccuracies or any suggestions you may have in the comments!

9 Responses to Living on the edge (of Rails) #5 – better eager loading and more

Avatar

Dr Nic

January 30th, 2008 at 8pm

In case you need any encouraging to continue with these posts: “Please continue with these posts”.

Or more succinctly:

+1

Avatar

otavio

January 30th, 2008 at 10pm

Hy chu, great post. First i tought that “:includes does pre-loading” will be awesome. Then i’ve started to fell a little bit worried about performance for big tables. Then i saw the benchmarks at the ticket and it looks great again :). But it’s not quite finished yet, is it?

Avatar

A Fresh Cup » Blog Archive » Double Shot #134

January 30th, 2008 at 10pm

[…] The joy of independence: trying to nail down an elusive contract. There might be room in my dance card coming up in the future if you’re interested in talking. […]

Avatar

Ropiku

January 30th, 2008 at 10pm

Vote it on Ruby Reddit.

Avatar

Daniel Fischer

January 31st, 2008 at 3am

Great stuff Chu! As Dr. Nic said “+1”!!!!

Avatar

Mark Wilden

January 31st, 2008 at 6am

A Cartesian join matches every record from one table with every record from another table. Suffice it to say that Rails doesn’t do this. :)

///ark

Avatar

Chu Yeow

January 31st, 2008 at 10am

@Dr Nic, Daniel Fischer: Thanks for the encouragement!

@otavio: Yes you’re right – it’s not finished yet (there are still some bugs to fix), but I thought I’d get the word out since the decision to switch to pre-loading has been made :).

@Mark Wilden: You’re right – I’m the one who needs to brush up on my set theory. I think what I really mean to say is that the strategy now is to avoid making monster joins involving multiple tables.

Avatar

How to Avoid Hanging Yourself with Rails

February 13th, 2008 at 10am

[…] Presentation – How to Avoid Hanging Yourself with Rails Faker – faker.rubyforge.org ‘mrj’s ActiveRecord select/include patch – http://dev.rubyonrails.org/attachment/ticket/7147/init.5.rb include preloading optimisation – http://blog.codefront.net/2008/01/30/living-on-the-edge-of-rails-5-better-eager-loading-and-more/ […]

Avatar

Living on the edge (of Rails) #11 | redemption in a blog

June 20th, 2008 at 1pm

[…] is another ActiveRecord performance boost related to the pre-loading any eager-loaded :includes mentioned previously. Basically what this patch does is to only join referenced tables when needed. It does this by […]