For the love of god, use the page cache
August 11th, 2007
Let me just say in advance, I realize this won’t work for everyone. However, it will work for an awful lot of people that mistakenly think they currently can’t use Rails’ page cache. Also, in some examples I’m simplifying for the common case.
If you don’t want to read all my pointless rambling, feel free to jump down to the solution.
What is this “page cache” you speak of?
Put simply, Rails will generate your page once and then write it out to disk where your web server can serve it up from then on, requests never even hit Rails. You can argue all you want about Rails’ speed but, a web server serving a static page will always be several orders of magnitude faster.
Neither the fragment or action caches receive this benefit, it is exclusive to the page cache.
But, but, but…
…I don’t know about your application but, my pages are too dynamic to use the page cache.
I hear this way too often from people that should know better. Most pages in most Rails applications I’ve seen could make use of the page cache. The first question is, should they?
Sometimes it’s not worth the added effort for a page with dynamic elements. A good example of this would be a site admin tool with users counted on one hand. In this case some heavy weight reporting pages may benefit but other pages may just be wasted effort.
That leaves us with public facing sites, you know, where we make all our money and what truly matters. Now, I know premature optimization is the root of all evil but, we’re all well acquainted with the Twitter mythology now aren’t we?
We all hope our sites will get huge and while I don’t think Rails is unusably slow, I do think we shouldn’t ignore the tools Rails gives us. It’s tough to argue when you see a non-cached page happening at ~30 pages per second and the same cached page at over 800 per second.
We know the enemy and he is Bob
So you want every page to say “Hello Bob!” (assuming your user’s name is Bob)? Damn, we can’t use the page cache unless of course only Bobs may sign up for the site.
Now, we could use the fragment cache for big portions of the of the page but that’s not nearly as big a win as the page cache. Using the fragment cache still goes through the Rails request handler, it’s filters, session retrieval, the action, any ActiveRecord queries and the page rendering. You only save in portions of the page rendering.
Going counter-culture
So what’s the solution here? Ajax. Yes, I’ve read all those articles about how so much ajax on “Web 2.0” sites is putting additional, unnecessary strain on their infrastructure. The conclusion in most of these articles is “use ajax sparingly.” I don’t disagree, it’s good advice. There are cases though where the wins are much bigger than what you’re losing, this is of course one of them.
The solution
The specifics of the solution will differ based on the demands of your page but, the basic idea is common.
- Create a new action for serving up the dynamic content.
Depending on your needs this could be a standard action with it’s corresponding ERB or it could be an action using RJS (to update multiple parts of the page). This action should do it’s work based on information retrieved from the session not URL parameters.
- Use javascript (jQuery, Prototype or Rails’ helpers) to make a request to that action from the page to be cached.
Here’s an example (using jQuery) from a site I built:
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$("#tinymenu2").load("<%= url_for :controller => 'member', :action => 'controls' %>");
});
</script>
- Enable page caching and revel in Ludicrous Speed.
What does this really give me?
Quite a bit. Like actually using Rails. A lot of Rails optimization articles I’ve read contained such gems as:
Avoid the use of dynamic URL generation (link_to, url_for) since rails needs to look up the routes table and that may take time. Just hard code the controller name and the action.
and
Try to avoid the excess use of helpers since it adds overhead.
In my opinion, if you’re doing either of those two things, you might as well switch back to PHP (or ASP or Java). Rails is about convenience and all these helpers help you as a developer. The various helpers in Rails keep things DRY and insulate your application from future changes.
The key is to be familiar with and use every tool Rails provides you with. With page caching combined with ajax you can use helpers as much as your heart desires and still have a very responsive app.
Conclusion
In the common case this approach will result in a massive speed increase. Also, since most javascript libraries can trigger when the DOM is ready (before images have loaded) and the result of the ajax request is likely quite small, the visual impact will be negligible (on my site the user specific ajax elements appear long before the page has finished loading).
So, what’s everyone think? I’ve also been trying to think of ways to pluginize this technique but it seems very application specific. If anyone has any ideas on that front, drop me a line.
Also, if there’s any interest, I’ll put together a simple application demonstrating the approach.
4 Responses to “For the love of god, use the page cache”
Sorry, comments are closed for this article.
August 19th, 2007 at 05:40 PM Rich, Perhaps I am just being dense here, but where do you put that line of javascript at? In the application layout? I guess am I just not familiar with jquery so the whole script looks a bit odd to me. P.S. This is Queso from irc
August 19th, 2007 at 08:13 PM Depends on what you're trying to accomplish. In my case, it's an element that goes on every page of the site so, yes, I have it in the application.rhtml. In other cases it may be in a controller specific layout or even a straight view file.
August 21st, 2007 at 11:06 AM I love this idea and I'm going to be using it on my next project, which is a small portfolio site for my father's artwork. I want him to be able to update his site very easily, and what's easier than providing administration functionality to the public view of the website? The problem is that this kills page caching, but not when using this method. He'll login, which will set a cookie, and javascript will detect that cookie and trigger some fancy JS which will render the admin stuff like an "edit" and "delete" link next to each piece of artwork -- all on a cached page! yay! Here's hoping it works ;)
August 22nd, 2007 at 04:21 PM Brent, it works like champ! We used this on the Web 2.0 Show which also has full page caching. Our public interface is our admin. We inject links for add new and edit episode.