Adding Page Caching to Sitemaps
The shortest distance between two points is not a straight line. The shortest distance is zero. Admittedly, we’re not about to fold space or do time travel, but you get the idea.
Likewise, for any web framework, the fastest way to deliver content to clients is to not use the framework at all, but let the webserver serve static files to clients.
In Rails this means using page caching and leave the heavy lifting to the webserver. Keeping your cache fresh and still maintaining stellar performance might seem like a daunting task, but Rails gives you quite a bit of help, so it is actually not that hard to do.
In this post I will take you through the steps to add page caching to our Sitemap, that we created in Creating Sitemaps with Comatose CMS.
Enable page caching for our Sitemap
As you can see below, enabling page caching is as simple as just adding one extra line in a controller
1 ## app/controllers/info_controller.rb 2 class InfoController < ApplicationController 3 caches_page :sitemap 4 def sitemap 5 respond_to do |format| 6 format.xml do 7 @website_url = 'http://example.com' 8 @pages = ComatosePage.find(:all, :conditions => "full_path not like ''") 9 render :action => 'sitemap', :layout => false 10 end 11 end 12 end 13 end
Verify caching
To verify that we are have succesfully enabled page cache, let’s update the development environment to mimick the production environment.
1 ## config/environments/development.rb 2 config.action_controller.perform_caching = true
Then we need to restart the server, do a request to http://localhost:3000/sitemap.xml and you should see something like the following in your log.
Processing InfoController#sitemap (for 127.0.0.1 at 2009-03-17 17:04:26) [GET]
Parameters: {"action"=>"sitemap", "controller"=>"info"}
SQL (0.2ms) SET NAMES 'utf8'
SQL (0.1ms) SET SQL_AUTO_IS_NULL=0
ComatosePage Load (1.5ms) SELECT * FROM `comatose_pages` WHERE full_path not like ''
Rendering info/sitemap
ComatosePage Columns (2.0ms) SHOW FIELDS FROM `comatose_pages`
Cached page: /sitemap.xml (0.7ms)
Completed in 16ms (View: 9, DB: 4) | 200 OK [http://localhost/sitemap.xml]
The line we’re looking for is Cached page: /sitemap.xml (0.7ms)
In your public folder you should also see a sitemap.xml file.
If everything is configured correctly, subsequent requests to /sitemap.xml should not show up in the applications log, as the webserver is now serving the static file and not using Rails at all :-)
Add a cache sweeper
Now that we can succesfully cache the sitemap, we need to make sure it stays fresh, when content in Comatose is updated.
Rails provides us with Cache Sweepers, which is kind of like an observer, but specialised. Let’s create a cache sweeper that observes ComatosePage, and deletes the cached sitemap.xml when pages are saved or destroyed.
1 ## app/sweepers/comatose_sweeper.rb 2 class ComatoseSweeper < ActionController::Caching::Sweeper 3 observe ComatosePage 4 5 def after_save(page) 6 expire_cache(page) 7 end 8 9 def after_destroy(page) 10 expire_cache(page) 11 end 12 13 def expire_cache(page) 14 # Use the ApplicationController, as were outside the scope 15 # of the executing controller 16 ApplicationController.expire_page( "/sitemap.xml" ) 17 end 18 end
Finally, we need to tell Comatose to use the sweeper.
1 ## config/initializers/comatose.rb 2 # set a cache sweeper to react when comatose expires pages from it's page caching 3 ComatoseAdminController.send :cache_sweeper, 4 :comatose_sweeper, :only => 'expire_cms_page'
Verify cache sweeper
To verify that your cache sweeper is working, all you need to is to is to save a page in Comatose and then verify the absense of sitemap.xml from the public folder.
Finally
That’s it! You know have a Comatose based Sitemap that will only be generated when there are changes.
P.S. Don’t forget to set your development environment to NOT use caching anymore.