views:

975

answers:

2

My Rails application is running on a VM. The VM's performance is just fine with static pages. In fact, I'm running another site using Apache virtual hosting that is just serving up static HTML files and the response is adequate. However, my Rails application that is dynamically generating XML files responds very slowly. In fact, it takes about 10 seconds or so for each XML file. These XML files that Rails generates do not change more than once a day.

What is the best practice to configure these XML files to be cached?

Edit 1:

I should mention that these XML files are not viewed by a browser. They are viewed by mobile applications in the "field." So, unfortunately sending "HTTP/1.0 304 not modified" won't work.

Edit 2:

If it matters, I'm using Phusion Passenger to host my Rails app.

A: 

Send a "HTTP/1.0 304 not modified" HTTP status code to the client when the XML document is requested.

karim79
+6  A: 

If you're using rails' static page caching and serving through apache, just using an explicit xml extension on the urls would do it.

if you're only serving xml and no html you might also edit the apache conf to default to xml instead of html when looking for cached files.

cache expiry is a rather boring thing to code and test, but since you're seldom regenerating the files, you might just expire the entire cache.


Here's a trimmed selection of files and excerpts from how I handle cache in a small, seldom updated rails site:

In the controllers you want to cache

class XmlThingController < ApplicationController
  caches_page :index, :show, :other_actions

In the controllers/actions that modify data that would cause changes to the xmls:

class Admin::SomeCrudController < AppplicationController
  cache_sweeper :stupid_master_sweeper, :only => [ :save, :destroy ]

In 'config/environments/production.rb'

config.action_controller.page_cache_directory = 
  File.join(RAILS_ROOT, 'public', 'cache')

Somehere in your vhost apache conf:

# 1.4. Handle caching

# 1.4.1. prevent direct cache access
RewriteRule  ^/cache - [F,L]

# 1.4.2. for index
RewriteCond  %{DOCUMENT_ROOT}/cache/index.html -f
RewriteRule  ^/?$ /cache/index.html [L]

# 1.4.3. for explicitly specified extensions
RewriteCond  %{DOCUMENT_ROOT}/cache%{REQUEST_URI} -f
RewriteRule  ^(.*)$ /cache$1 [L]

# 1.4.4. with html extension implied
RewriteCond  %{DOCUMENT_ROOT}/cache%{REQUEST_URI}.html -f
RewriteRule  ^(.*)$ /cache$1.html [L]


# 1.5. Finally, proxy everything else to mongrel
RewriteCond  %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule  ^/(.*)$ balancer://your-website-proxy%{REQUEST_URI} [P,QSA,L]

The dumb sweeper that cleans that entire cache every time it's triggered:

class StupidMasterSweeper < ActiveRecord::Observer
  observe Foo, Bar # All models that affect caching here

  def after_save(record); end
  def after_destroy(record); end

  def filter(controller)
    # sweep everything.
    `cd #{RAILS_ROOT} && RAILS_ENV=#{ENV['RAILS_ENV']} rake cache:clear`
  end
end

lib/tasks/cache.rake

namespace :cache do 

  desc "Remove all cached files" 
  task :clear do
    puts `rm -rf #{RAILS_ROOT}/public/cache/*`
  end 

end


If you prefer to default the default implied extension to xml, change the extension on the 1.4.2 index rule, and the following:

# 1.4.4. with html extension implied
RewriteCond  %{DOCUMENT_ROOT}/cache%{REQUEST_URI}.html -f
RewriteRule  ^(.*)$ /cache$1.html [L]

to:

# 1.4.4. with xml extension implied
RewriteCond  %{DOCUMENT_ROOT}/cache%{REQUEST_URI}.xml -f
RewriteRule  ^(.*)$ /cache$1.xml [L]
kch
... as soon as i'm dony playing with cornify.
kch
Interesting solution. Sound easy enough. What is meant by "rails' static page caching" is that a gem or something? Or, is that built into Rails?
JP
It's built-in, see caches_page in the updated answer.
kch
Does it matter if I'm using Phusion Passenger?
JP
You'll have to change the apache conf bit that proxies the request (1.5) to do whatever mod_rails needs to be done. But otherwise everything remains the same.
kch
I have no experience with mod_rails, but I hear it's supposed to be all very automagic, so maybe you just remove the proxying bit, and hopefully the unchanged URL ends up being handled by it.
kch