January 25, 2013
3 min read time

Switching behavior dynamically in Varnish

From time to time we see the need for Varnish to switch behavior based on an external event. Let's have a look at how that can be done. 

Some time ago I was asked how a ticket auction site should deal with massive traffic spikes. These sites can get an horrendous amount of traffic in a rather short time frame and the content is in its nature quite dynamic. This is what advice we gave them in order to quickly build something that would scale.

Firstly we recommend to cache everything. You don't have to cache it for a long period, just caching stuff for a second will reduce the load on the backend from hundreds or thousands per second to one single request. That alone can make a huge difference.

They also wanted to be able to change the behavior of the caching layer depending on whether the concert was sold out or not. The ideal place to do this would obviously be in the response headers coming from the backend but for some reason they couldn't or wouldn't do that. What to do?

One of the hidden gems of Varnish Cache is Tollefs Variable VMOD. It is a really simple piece of code that very easily gives you access to variables, or rather associative arrays in VCL.

Lets say the site had a URL structure like this site.com/tickets/$EVENT - where $EVENT would be a unique identifier for the event. Normally we would enforce a one second TTL on everything below /tickets/. In case the event is sold out we can set the TTL to one hour.

This would consist of two parts. First the part that governs the TTL:

in vcl_fetch:

import var;
(..)
if (req.url ~ ^/tickets/) {
set beresp.ttl = 1s;

Now we have enforced the one second TTL. We now would like to override the sold out events.

   # Pick the event out of the URL and place it in a var:
   var.set("event", regsub(req.url, "/tickets/([^/]+).*", "\1"));
   if (var.global_get(var.get("event") == "soldout") {
      set beresp.ttl = 1h;
   }
}

That wasn't so hard. Now we only need a way to set the event as sold out. We define /set/$EVENT/$STATUS to set the $EVENT to $STATUS. In real life you probably want to protect this part with ACLs.

in vcl_recv:

if (req.url ~ "/set/[^/]+/[^/]+" {
   var.set("event", regsub(req.url, "/set/([^/]+).*", "\1"));
   var.set("status", regsub(req.url, "/set/[^/]+/([^/]+)", "\1"));
}

Now, the moment an event sells out they call /set/$EVENT/soldout and change the TTL to one hour.

You should note that the variable VMOD stores the variables in a simple list. This works really well as long as the amount of variables is rather small. If you plan on storing a huge number of variables you'd probably need to rewrite the variable vmod to use a more advanced data structure.  I'm pretty sure Tollef will be happy for a patch. 

Picture is (c) DBduo Photography used under a CC license.