Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1

220 | Chapter 7: REST, Resources, and Web Services


latest version; if they match, the client has the latest version and the server renders a
simple 304 Not Modified response.


Often, the ETag is a hash of the response body, but it can be anything that is likely to
change when the body does. Another common method used for static content is to
use some combination of the file’s inode number, last-modified time, and size; this is
very efficient because all of that information can be determined with astat(2)
syscall. Of course, this method breaks down in clusters (where the static files might
span filesystems) or when serving dynamic content.


Rails has built-in support for transparently generating ETags. The method used is the
“simplest thing that could possibly work”; it is generic because it makes no assump-
tions about the structure or semantics of an application. Its implementation is sim-
ple enough that it can be shown here:


def handle_conditional_get!
if body.is_a?(String) &&
(headers['Status']? headers['Status'][0..2] == '200' : true) &&
!body.empty?
self.headers['ETag'] ||= %("#{Digest::MD5.hexdigest(body)}")
self.headers['Cache-Control'] = 'private, max-age=0, must-revalidate' if
headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control']

if request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag']
self.headers['Status'] = '304 Not Modified'
self.body = ''
end
end
end

On each request with a 200 OK response, Rails generates an MD5 hash of the
response body and sets it as the ETag (unless one has already been set; this allows
you to provide custom ETags simply by setting them). It also sets theCache-control:
must-revalidateflag, which instructs caches (including caching user agents, such as
web browsers) to revalidate the cache against the ETag, using theIf-None-Match
header. This ensures that the generated ETags are actually used. Finally, if a response
comes in with anIf-None-Matchheader, and its value is the same as the generated
ETag, the response body is cleared and rendered with a 304 Not Modified status.


The default Rails ETag mechanism works well and is completely transparent, but it
has some drawbacks. The main limitation is that the response body must still be gen-
erated (which often means several trips to the database, template rendering, URI
generation, and, finally, MD5 hashing) before a decision can be made. If the client’s
version was actually the latest, all of that work is discarded. Therefore, the default
ETag implementation only saves bandwidth, not CPU time or I/O. We must get into
more application-specific caching methods in order to avoid rendering a response
altogether.

Free download pdf