![http://travis-ci.org/russ/lacquer](https://secure.travis-ci.org/russ/lacquer.png?branch=master) # Lacquer Rails drop in for [Varnish](https://www.varnish-cache.org/) support. ## Install This gem requires ruby 1.9+ ```text gem install lacquer rails generate lacquer:install ``` **config/initializers/lacquer.rb** ```ruby Lacquer.configure do |config| # Globally enable/disable cache config.enable_cache = true # Unless overridden in a controller or action, the default will be used config.default_ttl = 1.week # Can be :none, :delayed_job, :resque, :sidekiq config.job_backend = :none # Array of Varnish servers to manage config.varnish_servers << { :host => "0.0.0.0", :port => 6082 # if you have authentication enabled, add :secret => "your secret" } # Number of retries config.retries = 5 # config handler (optional, if you use Hoptoad or another error tracking service) config.command_error_handler = lambda { |s| HoptoadNotifier.notify(s) } ### Varnish - 2.x / 3.x .. VCL-Changes ### https://www.varnish-cache.org/docs/trunk/installation/upgrade.html # => Purge Command ( "url.purge" for Varnish 2.x .. "ban.url" for Varnish 3.x ) # => purges are now called bans in Varnish 3.x .. purge() and purge_url() are now respectively ban() and ban_url() config.purge_command = "ban.url" # => VCL_Fetch Pass Command ( "pass" for Varnish 2.x .. "hit_for_pass" for Varnish 3.x ) # => pass in vcl_fetch renamed to hit_for_pass in Varnish 3.x config.pass_command = "pass" end ``` **app/controllers/application_controller.rb** ```ruby class ApplicationController < ActionController::Base include Lacquer::CacheUtils end ``` **config/varnishd.yml** ```yaml development: listen: localhost:3001 telnet: localhost:6082 sbin_path: /usr/local/sbin # path to varnishd bin_path: /usr/local/bin # path to varnishadm storage: "file,#{Rails.root}/log/varnishd.#{Rails.env}.cache,100MB" test: listen: localhost:3002 telnet: localhost:6083 sbin_path: /usr/local/sbin bin_path: /usr/local/bin storage: "file,#{Rails.root}/log/varnishd.#{Rails.env}.cache,100MB" production: listen: :80 telnet: localhost:6082 sbin_path: /usr/local/sbin bin_path: /usr/local/bin storage: "file,#{Rails.root}/log/varnishd.#{Rails.env}.cache,100MB" params: overflow_max: 2000 # for Varnish 2.x ... use "queue_max: 2000" for Varnish 3.x thread_pool_add_delay: 2 thread_pools: 4 # thread_pool_min: 200 # <800/number of cpu cores> thread_pool_max: 4000 ``` If only some urls of the application should be cached by varnish, Lacquer::CacheControl will be helpful. **config/initializers/caches.rb** ```ruby require "lacquer/cache_control" Lacquer.cache_control.configure do |config| config.register :static, :url => "^/images", :expires_in => "365d" config.register :static, :url => "^/stylesheets", :expires_in => "365d" config.register :static, :url => "^/javascripts", :expires_in => "365d" config.register :class_section, :url => "^(/[a-z]{2})?/(info_screens|class_sections)/%s.*$", :args => "[0-9]+", :expires_in => "1m" config.register :open_scoring, :url => "^(/[a-z]{2})?/class_sections/%s/open_scoring.*$", :args => "[0-9]+", :expires_in => "1m" end ``` In the sweeper we can do something like this ```ruby class_section = ClassSection.find(1) Lacquer.cache_control.purge(:open_scoring, class_section) ``` This will purge `"^(/[a-z]{2})?/class_sections/1/open_scoring.*$" (/sv/class_sections/1/open_scoring.js, /sv/class_sections/1/open_scoring.html)` The varnish.vcl is preprocssed when starting varnishd with the rake tasks `rake lacquer:varnishd:start` **config/varnish.vcl.erb** ```vcl sub vcl_recv { # Lookup requests that we know should be cached if (<%= Lacquer.cache_control.to_vcl_conditions %>) { # Clear cookie and authorization headers, set grace time, lookup in the cache unset req.http.Cookie; unset req.http.Authorization; return(lookup); } # Generates # # if(req.url ~ "^/images" || # req.url ~ "^/stylesheets" || # req.url ~ "^/javascripts" || # req.url ~ "^(/[a-z]{2})?/(info_screens|class_sections)/[0-9]+.*$" || # req.url ~ "^(/[a-z]{2})?/class_sections/[0-9]+/open_scoring.*$") { # unset req.http.Cookie; # unset req.http.Authorization; # return(lookup); # } } sub vcl_fetch { <%= Lacquer.cache_control.to_vcl_override_ttl_urls %> # Generates # # if(req.url ~ "^/images" || req.url ~ "^/stylesheets" || req.url ~ "^/javascripts") { # unset beresp.http.Set-Cookie; # set beresp.ttl = 365d; # return(deliver); # } # # if(req.url ~ "^(/[a-z]{2})?/(info_screens|class_sections)/[0-9]+.*$" || # req.url ~ "^(/[a-z]{2})?/class_sections/[0-9]+/open_scoring.*$") { # unset beresp.http.Set-Cookie; # set beresp.ttl = 1m; # return(deliver); # } } ``` This makes it much simpler to perform cacheing, it's only setuped in one place, purge it or just let it expire. ## Usage To set a custom ttl for a controller: `before_filter { |controller| controller.set_cache_ttl(15.minutes) }` Clearing the cache: ```ruby class Posts < ApplicationController after_filter :clear_cache, :only => [ :create, :update, :destroy ] private def clear_cache clear_cache_for( root_path, posts_path, post_path(@post)) end end ``` Control varnishd with the following rake tasks ```text rake lacquer:varnishd:start rake lacquer:varnishd:stop rake lacquer:varnishd:restart rake lacquer:varnishd:reload rake lacquer:varnishd:status rake lacquer:varnishd:global_purge ``` ## Deployment Lacquer supports Capistrano 2.5+ For Capistrano 2.5 just add `require 'lacquer/capistrano'` to your `config/deploy.rb` file. For Capistrano 3+ add `require 'lacquer/capistrano'` to your `Capfile`. ```ruby # Capfile require 'capistrano/setup' require 'capistrano/deploy' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'lacquer/capistrano' ``` Adding this will give you the following cap tasks ```text cap lacquer:global_purge # global_purge varnish cap lacquer:restart # restart varnish cap lacquer:start # start varnish cap lacquer:status # status varnish cap lacquer:stop # stop varnish ``` ## Gotchas The default TTL for most actions is set to 0, since for most cases you'll probably want to be fairly explicit about what pages do get cached by varnish. The default cache header is typically: Cache-Control: max-age=0, no-cache, private This is good for normal controller actions, since you won't want to cache them. If TTL for an action is set to 0, it won't mess with the default header. The key gotcha here is that cached pages strip cookies, so if your application relies on sessions and uses authenticity tokens, the user will need a session cookie set before form actions will work. Setting default TTL to 0 here will make sure these session cookies won't break. As a result, all you have to do to set a cacheable action is the before filter above. ## Note on Patches/Pull Requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. ## Copyright Copyright (c) 2015 Russ Smith. See LICENSE for details.