Nick Sieger brings up an interesting question. Imagine an n-tier application and you’re using HTTP to connect the tiers together. That wasn’t the question, just a good practical advise to follow. The question was, can you place a proxy server between the tiers and reap all the benefits?
Specifically in the context of Rails and ActiveResource:
The great thing is that existing HTTP reverse proxies can be used without having to mix the caching code in with your application code.
You can get a cached installed in a few minutes, switch over by setting HTTP_PROXY, and bingo. Could it be that easy?
The Glass Half Empty
If you’re serving the same data to all your clients and there’s little transformation happening on the client, then a reverse caching proxy is the way to go. It’s as close as it gets to a free lunch, performs and scales better than loading the database.
It breaks in two cases, though.
The first, when you’re serving different content to different clients on the same resource, and you don’t want the proxy server leaking those secrets away. Say you’re authenticating, admins get a different view than pedestrians, and both are served by the same URL. Rails assumes that by default, a good assumption to make, and sets Cache-Control to private, telling the proxy not to cache anything. It still allows caching in the client, but you’re probably not running a caching client.
The second place it breaks is when the client does non-trivial transformation on the data it receives. Imagine a client pulling in 10MB of data to calculate ‘total of orders’. Hitting the cache when there’s no change could save a trip to the database, but you still end up processing all that data again.
I’m saying “could” because the Rails magic involves running the action in full, creating a response document, calculating the ETag and then deciding not to send it back. On the Web that makes a noticeable difference in client response time, your browser will thank you. When you’re running two machines in the data center or even same rack, you won’t see any performance or scalability gains. It’s still database access all the way down.
The Glass Half Full
All of which is fairly easy to fix, for Rails or any other framework, but requires that we remove some of the child-proofing and expect adults to pay more attention to caching. Not horribly complex, but no transparent benefits either.
cache_in_public. If you only ever return the same content for a given resource, but possibly 403/404 for unauthorized clients, you can change Cache-Control to public/must-revalidate. My HTTP sources tell me this will work for a caching proxy without leaking to rogue clients. Something like:
cache_in_public :index, :show
acts_as_sourced. If you do significant work on the client, you want to handle caching directly, sending ETag/Last-Modified and avoiding redundant work on unchanged data. This can be simplified by creating a plugin that handles conditional GETs:
class Computed < ActiveRecord::Base acts_as_sourced end Computed.retrieve(url).save
if_modified. Now that you’re bona fide caching all over the place, or as often as you possibly can, you’ll want to eliminate those round trips to the database by checking for changes before doing any work. Something like if_modified:
def show()
if_modified @record do
render :action=>'show'
end
end
While we’re at it, paying attention to caching and having fun, we should also consider conditional PUTs:
resource.update do |record| record.count += 1 end
Side Effects
And those are quite unfortunate, so we’ll have to keep that in mind.
You see, caches are side effects. Any request going through a cache will not necessarily return the correct result. That depends entirely on the state of the cache and how resources are handled by the server. How do you prove the code is correct?
More than server-side and client-side support, this will need a good testing framework to make sure the cache abstraction doesn’t leak, and developer awareness for what needs to be tested. It’s not rocket science, but there still is a barrier to entry of Memcached complexity, though with more benefits.