A tiny but useful update was just added to edge Rails that lets you specify a block for which a query cache will be present. The query cache will cache select statements and guarantee that identical select statements will only hit the database once during the block. Any inserts, updates or deletes will clear the cache as the result of any cached queries may have changed.
1 2 3 4 5 6 |
User.cache do me = User.find(1) # DB hit again = User.find(1) # Cache hit me.update_attribute(:name => 'Not Ryan') # Cache cleared me = User.find(1) # DB hit end |
While you might not think this is very useful in your various controller methods which should be of limited scope, think of your views that may call the same authentication queries several times within the view body. While a simple @is_admin || = user.get_role('ADMIN') custom cache mechanism would work just fine, you now have the options to wrap your view within a query cache and have it handled for you.
The query cache is keyed on the raw SQL statement, so it’s very minimalist in its complexity. It doesn’t know that your identical query with different order conditions is the same query as before – and will hit the db again. It only caches identical SQL statements, literally.
tags: rubyonrails, rails, activerecord caching

Strictly @is_admin || = user.get_role(‘ADMIN’) has different semantics than query caching, as it returns the same regardless of intermittent updates.
Couldn’t have come too soon… I’ve just spent the past few days optimising AR-triggered SQL statements because of very complex business logic. This should make my life a little easier…
The only other thing on my wishlist is automatic population of two-way relationships on eager includes (as AR is only aware of rels in one direction).
I love the idea of a caching block. However, I can’t help but feel like putting model caching concerns in the view is somehow wrong. The same thing goes for the fragment caching in Rails…it just feels dirty. Maybe I just need to stop worrying and learn to love the bomb.
Kevin – I hear you about the dirtiness factor.
However, maybe you could use an around filter in your controller to set up the cache block. Since views are rendered as part of the action invocation, your views should reap the benefits of the caching while the caching code is not explicitly in your view…?
But… isn’t query caching something the database does for you already? I use the MySQL query cache, for instance, and it works great. Nothing to change in my Rails apps at all. I wonder which method is faster…
Rails offers a lot of cool ways to cache. But I agree with Kevin’s comment, it feels dirty to do this in your app somehow. Seems like a job for the database and the web server?
Jeremy, some databases do this for you – like you said, MySQL does. However, even when you have a successful db query cache hit you’re still going across the wire to the db and you’re still processing the cached results you get back from the db. While by no means the most powerful solution, the Rails method is a little more db-independent.
I don’t have an issue with handling caching at the application layer; I just don’t like having it all intermingled with the MVC side of things. The way I see it, you should be able to define classes like PersonControllerCacher which will follow Railsesque naming conventions to know which controller to handle. In them you could define how to do your caching. For example, you could setup the around_filter approach that Ryan mentioned or have methods in the Cacher for the actions you want caching to occur for. The key is your controller wouldn’t have to know it is being cached. You could probably do something similar for views, except use CSS ids or classes to define fragments to cache. I’ve been thinking a good bit about this issue lately…maybe I’ll get around to writing a plugin to flesh out my ideas.
Has anyone though how useful this would be for Active Resource? Great stuff
One thing to keep in mind is that while the query is being cached, the ActiveRecord objects are not. Each MyModel.find(1) call still returns a new instance of MyModel for the same record.
I can’t help but feel that this smells a bit strange. I can see but a handful of situations where this would be useful and all of them feel very unrailsy to me. I suppose it’s nice to have this option if you really needed it, but I somehow doubt many people will.
I’ve very rarely seen somebody performing the exact same lookup multiple times in a block like that, so really things like the aforementioned user cache would be one of the few instances when you would really even want something like this, and even then it’s only worth doing because it flushes the cache after an insert or update; though even then it seems to me that it’d be a bit more useful if the affected keys in the cache were simply updated in the cache rather than the entire cache getting cleared.
If anybody else is using this feature in their app I’d be very interested to know it’s use case and how much this is actually saving them (ie how many lookups.)
It’s an interesting idea, I am just not sure if it’s one that should be part of Rails.
As of revision 6868 the caching is turned on for controller action processing by default. See the inclusion of the SqlCache module: http://dev.rubyonrails.org/browser/trunk/actionpack/lib/action_controller/caching.rb#L653
Correct me if I am wrong, but I don’t believe rails is currently smart enough to assign relationship when they are fetched through an association as is the case below.
E.G.
@dad.children.each |c| if c.dad.something? .... end end
This is a big performance issue in my opinion, especially when you are doing cascading deletes and doing things with relationships in the after/before_destroy filters. I suppose this new caching would help but it seems like a better solution for this would simply be to assign the record to the child relationship when the children are fetched through via the relationship.