When it comes to performance one of the most important considerations is caching of content. There are all sorts of approaches to the caching. Some protect the database from duplicate queries while others protect your application from having to perform an expensive algorithm over and over. Today I am going to talk about the most aggressive form of content caching when it comes to the web - full page caching.
There are two main types of content caching that I've seen people use, object/method caching and partial page caching. The former is often implemented using Memcached and a thin wrapper in front of your objects. This is extremely effective as it prevents the database from processing the same query over and over again. Partial page caching on the other hand is where you cache a fragment of a page. This is common in situations where a portion of a web page typically doesn't change. For example if you have a dynamic web application but the menubar is database driven and rarely changes... you can cache just that part of the page.
My first implementation of partial page caching was on this site when I began writing howto documents. I used xml+xsl to generate documentation as html fragments. I then used Python to inject them into the page at runtime. It worked really well... and made it easy to write fast loading documentation without having to process xsl at runtime.
After a while I wanted to try more aggressive content caching. I've always had an interest in performance and I knew my site could handle way more traffic serving flat html files than it ever could with even simple server side processing. I poked around a little and found that my howto pages only had two things that were really dynamic per page load. The first was a "processing time" snippet at the bottom of the page. The second was the navigation bar. For cats who were logged in they had a "logout" button. The reverse was true for everyone else. It was at this point that I had a slight paradigm shift.
Instead of thinking about how I could inject static content into a dynamic page, I started thinking about how I could inject dynamic content into static page. I decided to scrap the "processing time" snippet at the bottom on pages that were not processed by the server at all. Then to fixup the toolbar I focused my efforts on the majority of my traffic, 99% of which are people who never actually log in. I used a graphic that would change onmouseover() to indicate authentication status. So for cats not logged in (pretty much everyone) the graphic just sat there. For people who were logged in they would move their mouse over it and get a few useful links. The nice part was that an authenticated user could navigate to one of the 100% static howto pages and still have a navigation link to "my applications" or whatever.
For the current iteration of my site I decided to improve things by analyzing the various ways you can inject dynamic content into a static page:
|Ajax||A typical example would be to use an onload() event to perform an Ajax fetch for some information that's then added to the DOM in some way. The advantage of this approach is that it's very easy to implement, but it's really not helping anything. If every page load is going to request more data from the same domain then it's better to include it with the initial response. This approach can make sense, but not typically.|
|Event driven Ajax||This is the same as above, only the work happens based on user interaction. This is very common and can be an ideal way to fetch additional information as needed. For situations where the data isn't ever needed... it won't be fetched. I use this approach for the search box to provide an autocomplete list of tags.|
|Dynamic script block||This is also the same as above, only it supports cross site scripting. This approach is good when the data you need is from an external api. Instead of fetching external data server side and sending it with the first response... you let the client browser do that for you. This is the approach used to display my del.icio.us bookmarks on the right of this page. You can see what I'm currently reading, and my server doesn't have to process anything.|
|String replacement||This approach does involve server side processing, but it's fast and works against a fully cached page. The idea here is to use use simple string replacement to add additonal information to a cached page prior to sending it to the client. This is the approach I'm using to display "processing time" at the bottom of this page.|
By using a combination of the techniques above I've been able to keep things pretty dynamic while leveraging the speed benefits of full page caching. The result is that most pages (after my site has warmed up) process in less than 20 milliseconds on a 1.9GHz Athlon with 512 of ram. Combine that with a few other tricks and I think the result is a surprisingly snappy site considering it's served out of my spare bedroom :/
Cheers, and good luck making your site faster