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.
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.
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.
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 additional 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.
This approach uses a “display” cookie that holds data unique to each
user for the purposes of display. The idea here is to exploit the users
cookie to hold just enough information to make client side decisions.
Javascript can then use this information to influence how the page is
drawn. The really important thing is to only include information that is
perfectly safe for anyone to see. I use this approach to draw the “Sign
In” or “Sign Out” links in the navigation at the top of the page. If
you really want you can edit your cookie and coerce my site to display a
different link. For that matter you can also use Firebug to do the same
thing. I’m not including any information in the cookie that can be used
against me. But it does allow me to use an “authenticated” navigation
even on pages that are 100% static html.
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 :/