without_scope For the love of god, use the page cache
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 rack servers 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 rackmount cases 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.