I have a Ruby on Rails application fronted by varnish+nginx. As most of the sites content is static unless you are a logged in user, I want to cache the site heavily with varnish when a user is logged out but only to cache static assets when they are logged in.
When a user is logged in they will have the cookie 'user_credentials' present in their Cookie: header, in addition I need to skip caching on /login and /sessions in order that a user can get their 'user_credentials' cookie in the first place.
Rails by default does not set a cache friendly Cache-control header, but my application sets a "public,s-max-age=60" header when a user is not logged in. Nginx is set to return 'far future' expires headers for all static assets.
The configuration I have at the moment is totally bypassing the cache for everything when logged in, including static assets — and is returning cache MISS for everything when logged out. I've spent hours going around in circles and here is my current default.vcl
    director rails_director round-robin {
  { 
    .backend = { 
      .host = "xxx.xxx.xxx.xxx"; 
      .port = "http";
      .probe = {
        .url = "/lbcheck/lbuptest";
        .timeout = 0.3 s;
        .window = 8;
        .threshold = 3;
      }
    } 
  }
}
sub vcl_recv {
  if (req.url ~ "^/login") {
    pipe;
  }
  if (req.url ~ "^/sessions") {
    pipe;
  }
  # The regex used here matches the standard rails cache buster urls
  # e.g. /images/an-image.png?1234567
  if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
    unset req.http.cookie;
    lookup;
  } else {
    if (req.http.cookie ~ "user_credentials") {
      pipe;
    }
  }
  # Only cache GET and HEAD requests
  if (req.request != "GET" && req.request != "HEAD") {
    pipe;
  }
}
sub vcl_fetch {
  if (req.url ~ "^/login") {
    pass;
  }
  if (req.url ~ "^/sessions") {
    pass;
  }
  if (req.http.cookie ~ "user_credentials") {
    pass;
  } else {
    unset req.http.Set-Cookie;
  }
  # cache CSS and JS files
  if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
    unset req.http.Set-Cookie;
  } 
  if (obj.status >=400 && obj.status <500) {
    error 404 "File not found";
  }
  if (obj.status >=500 && obj.status <600) {
    error 503 "File is Temporarily Unavailable";
  }
}
sub vcl_deliver {
  if (obj.hits > 0) {
          set resp.http.X-Cache = "HIT";
  } else {
          set resp.http.X-Cache = "MISS";
  }
}
        - 
                        
Ok, in the end I managed to solve this using the following vcl file. Note that I added a couple of extra bits to allow cache expiration grace when the backend has died.
It seems my main failure was using
unset req.http.Set-Cookie;when I should have been usingunset obj.http.Set-Cookie;in thevcl_fetchsection. (objin vcl_fetch andreqin vcl_recv section).director rails_director round-robin { { .backend = { .host = "xxx.xxx.xxx.xxx"; .port = "http"; .probe = { .url = "/lbcheck/lbuptest"; .timeout = 0.3 s; .window = 8; .threshold = 3; } } } } sub vcl_recv { if (req.backend.healthy) { set req.grace = 30s; } else { set req.grace = 1h; } if (req.url ~ "^/login") { pipe; } if (req.url ~ "^/sessions") { pipe; } if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") { unset req.http.cookie; lookup; } else { if (req.http.cookie ~ "user_credentials") { pipe; } else { unset req.http.cookie; } } # Only cache GET and HEAD requests if (req.request != "GET" && req.request != "HEAD") { pipe; } } sub vcl_fetch { set obj.grace = 1h; if (req.url ~ "^/login") { pass; } if (req.url ~ "^/sessions") { pass; } if (req.http.cookie ~ "user_credentials") { pass; } else { unset obj.http.Set-Cookie; } # cache CSS and JS files if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") { unset obj.http.Set-Cookie; } if (obj.status >=400 && obj.status <500) { error 404 "File not found"; } if (obj.status >=500 && obj.status <600) { error 503 "File is Temporarily Unavailable"; } } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } }From davidsmalley - 
                        
I can't comment so I'm posting this as an answer.
Attention: As of 2.1.0, obj.* is called beresp.* in vcl_fetch, and obj.* is now read-only.
These also seem better placed in vcl_recv instead of vcl_fetch:
if (req.url ~ "^/login") { pipe; } if (req.url ~ "^/sessions") { pipe; }Lastly, in you backend definition, I would add
.max_connections = 32;Adjust to the number of passenger backends you have allowed nginx/apache to create. If you don't set this limit you should keep an eye on you passenger global queue (assuming you're using passenger).
From XCondE 
0 comments:
Post a Comment