views:

2450

answers:

3

When i deploy a rails application in production mode, it appends a date-time string as a query param to the end of all the static asset urls. This is to prevent browsers using old-out of date cahed copies of the assets after I redeploy the application.

Is there a way to make rails use the old time stamps for the assets that have not changed (and only the ones that have not changed) since the last deployment. I want to do this to prevent users having to redownload those assets that have not changed.

+4  A: 

I think you can use ENV['RAILS_ASSET_ID'] to alter the cache-busting asset ID. Unfortunately, this is for all assets.

But if it's not set, it uses the asset's source modification time. If that file hasn't been modified since the last time you used it, it shouldn't be a problem.

If the asset ID is changing when they haven't been changed, it might be because of your deployment process altering the modification time and maybe you could look at tweaking that.

Lastly, you can always override rails_asset_id with your own custom method.

Hope this helps.

Aupajo
A: 

@Aupajo I like this, but I think I can take it a bit further. The problem here is that capistrano does create new copies of all the files on every deploy, so all the cache-busting strings are changed. However an MD5 of the file would only change when the file content changes.

Of course, generating an MD5 is expensive and slow, but you can cache the MD5 of a file in memcache, (keyed on the change time, as if the time has changed the MD5 may have changed, but if the timestamp has not changed the MD5 will not have changed

Laurie Young
+8  A: 

Capistrano, by default, touches every file it considers an 'asset'. As you said, this means after every deploy rails thinks every asset has changed and browsers wil download a newer version every time.

You can disable this in Capistrano with the following setting

set :normalize_asset_timestamps, false

If you are using SVN then the modified dates for your files should reflect the dates they were last modified in your repository, so that should be perfect.

If you are using Apache, you can add something like this to really making caching work for you. This helps by telling the browser to rely on the "Cache Control" directives meaning if it knows the asset is cached, it won't even bother requesting it.

#Etags should be based on the file parameters only (default includes INode)
FileETag MTime Size  

#Rewrite stuff
RewriteEngine On  

#This sets the environment variable (is_versioned) when the URL query string
#looks like ?874353948543  or any string of digits
RewriteCond %{QUERY_STRING} ^[0-9]+$
RewriteRule ^(.*)$ $1 [env=is_versioned:true]  

<Directory /deployed-rails-app/public/ >
    Options -Indexes FollowSymLinks -MultiViews
    AllowOverride None
    Order allow,deny
    allow from all  

    #For files, force the browser to rely on cache-control directives and 
    #Rails asset timestamps by removing Etags and Last-Modified dates  

    #For all assets that aren't stamped by rails, cache them for ~ 3 hours
    Header set "Cache-Control" "max-age=10000"
    Header unset Etag
    Header unset "Last-Modified"  

    #For all assets that ARE stamped by rails, cache them for 30 days
    Header set "Cache-Control" "max-age=2592000" env=is_versioned

</Directory>

I've set my production server up this way and now return visitors only perform one request (Get /) which returns the dynamic content and all the assets (~ 40 - 50) are cached.

Daniel Beardsley