Posted by tobi — 06:52 PM Feb 14
Rails 2.0 is getting its first bunch of new features. One of the first additions was a new set of clothes for the query cache. David started this feature quite a while ago but never finished it and it wasn’t activated.
Here is the catalyst which prompted the development:
Blog.find(1).articles.each { |a| puts "#{a.blog.title} #{a.title}" }
=> Blog Load (0.000461) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Article Load (0.000521) SELECT * FROM articles WHERE (articles.blog_id = 1)
=> Blog Load (0.000461) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Blog Load (0.000460) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Blog Load (0.000469) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Blog Load (0.000460) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Blog Load (0.000462) SELECT * FROM blogs WHERE (blogs.id = 1)
[..]
Painful stuff. This is because each belongs_to has its own cache. So for each article the blog object has to be loaded again even though we just received a perfectly capable set of data from the database a second earlier.
With a recent change you can now do the following:
Blog.cache do
Blog.find(1).articles.each { |a| puts "#{a.blog.title} #{a.title}" }
=> Blog Load (0.000461) SELECT * FROM blogs WHERE (blogs.id = 1)
=> Article Load (0.000521) SELECT * FROM articles WHERE (articles.blog_id = 1)
end
For the duration of the cache block the same query will not be run twice ( unless you run any INSERTS or UPDATES, in which case all the cache is flushed to disk )
For Rails 2.0 we hope to cultivate these humble beginnings into an automatic cache so that all read queries are only run once per rails request if possible.

Chad Humphries 14 Feb 22:19
Quite nice
Danger 15 Feb 00:20
Excellent! One of the most frustrating things in my log files is this collection of “Blog Load” queries exactly like that. I’m happy to be rid of them!
Eric Allam 15 Feb 00:21
Why isn’t ActiveRecord performing this way by default (in 2.0)? Would there ever be an instance for WANTING to retrieve the same blog from the DB more than once when you are just iterating through a has_many relationship? Maybe I am just confused.
Alex 15 Feb 02:32
Eric: It will be default in 2.0 – It has been in Rails for a while and not on by default in these earlier versions because it was not ready.
Sam 15 Feb 12:00
Eric: Because a Rails application is a long-lived process, and you might have several of them going on at the same time, you need a way to expire the cache (read IdentityMap).
In an O/R Mapper like Hibernate this is accomplished by scoping the IdentityMap to a “Session” aka a short-lived connection-like facility.
In Rails the IdentityMap could probably easily be scoped to the individual Request automagically, with no extra effort on the part of the user.
Nate 23 Feb 13:50
That is fantastic. It’s really sucked that doing a:
Parent.find(1).children.first.parent
Has to go get the parent twice.
anon 27 Feb 12:30
testing typo blog