Grace in Varnish 4

STALE-WHILE-REVALIDATE SEMANTICS IN VARNISH

In other parts of the caching world this behaviour is known as stale-while-revalidate. It's been possible to do with earlier versions of Varnish using the somewhat appropriately named "evil backend hack", having a backend which is always down, setting it as the backend for the request, letting grace kick in and restarting the transaction. I'm happy to say we no longer need evil hacks to do this in Varnish 4.0.
Let’s have a look at the VCL.

sub vcl_hit {
    if (obj.ttl >= 0s) {
       return (deliver);
    }
    if (obj.ttl + obj.grace > 0s) {
        return (deliver);
    }
    return (fetch);
}

Varnish Cache will prefer a fresh object, but when one cannot be found Varnish will look for stale one. When it is found it will be delivered and Varnish will kick off the asynchronous request. It is serving the request with a stale object while refreshing it. It is basically stale-while-revalidate, but it doesn't ahere to RFC5861. I've made a note to create VCL that would make Varnish actually grok the stale-while-revalidate and stale-while-error headers and I'll post this another day.
It is worth noting that you will must make each object a candidate for grace by setting beresp.grace in vcl_backend_response. Below and you'll see how it is done.


Extending grace when the backend is down
In Varnish 3.0 you would typically see configuration like this:

sub vcl_recv {
    if (req.backend.healthy) {
       req.grace = 10s;
    } else {
        req.grace = 1h;
    }
}

This would increase the grace period if the backend is detected as being sick. This makes it possible to get a cup of coffee and contemplate life and your attitude towards your job before actually fixing your backend. Since Varnish 4.0 is has somewhat different behaviour let’s see how this would look in Varnish 4.0.

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # normal hit
        return (deliver);
    }
    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint)) {
        # Backend is healthy. Limit age to 10s.
        if (obj.ttl + 10s > 0s) {
            set req.http.grace = "normal(limited)";
            return (deliver);
        } else {
            # No candidate for grace. Fetch a fresh object.
            return(fetch);
        }
    } else {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s) {
            set req.http.grace = "full";
            return (deliver);
        } else {
            # no graced object.
            return (fetch);
        }
    }
}

In the VCL above I set a custom header, grace, to indicate how much grace is involved in the delivery of each response. It made testing the VCL a lot simpler. To make Varnish actually deliver the grace header in the response I added this:


sub vcl_recv {
    set req.http.grace = "none";
}

This sets grace to none. Unless it is overridden it will stay at none. In addition I need to copy the grace header from the request object (req) to the response before delivering it.

sub vcl_deliver {
    set resp.http.grace = req.http.grace;
}

I of course also need to enable grace on in incoming objects.

sub vcl_backend_response {
    set beresp.ttl = 10s;
    set beresp.grace = 1h;
}

That's it. With this VCL Varnish will be truly elegant and you'll be able to enjoy your coffe while fixing your server.

Download one of our on-demand webinars and learn more Varnish Cache and Varnish Plus tips and tricks.

Download the webinar

6/20/15 11:19 AM by Per Buer

All things Varnish related

The Varnish blog is where the our team writes about all things related to Varnish Cache and Varnish Software...or simply vents.

SUBSCRIBE TO OUR BLOG

Recent Posts

Posts by Topic

see all