Search code examples
varnishvarnish-vcl

Use Grace Mode for banned objects


We are trying to improve our varnish caching setup. Our backend application is fairly slow so we want to maximize the hit rate for visitors. It is not generally an issue if cached content is outdated for a little while, so response performance has priority over currentness here.

The basics are these, and they are easy to implement with the usual VCL setups:

  • Content not present in the cache will be retrieved as usual from the backend.
  • Content present in the cache within its TTL will be delivered from the cache.
  • Content present in the cache but older than TTL should be delivered in Grace Mode, i.e. the cache will be updated in the background while the stale content is delivered to the client. (For our scenario the grace period would be fairly long so the visitor is ensured to get a cached result. We'll employ some form of cache warming to cache fresh content when needed.)
  • To make backend updates visible we use fairly usual bans like these: ban("req.http.host ~ (?i)[www\.example\.com|www\.example\.net] && req.url ~ ^/products/?$");. Our project has many domains and URLs so this affords us a reasonable amount of specificity so we don't have to invalidate the entire cache all the time.

Now comes the bit we are struggling with. When a ban() is issued it will fully invalidate the cached objects. The next request will be passed through to the backend. We would like to prevent that.

What we would like is a Grace Mode-like handling in this case. When we tell Varnish "forget all URLs beginning with /products" it should use the existing cached objects, but treat them as if the TTL had expired. So the first visitor to an affected URL will get the stale cached result (immediately, while Varnish retrieves a new result), but the second visitor will get a fresh result.

I understand that we could do something like this with req.hash_always_miss if we are working on a single object, but how can we do this with a large number of objects/URLs? Due to the structure of the project we do not have an exhaustive list of all URLs. And it seems pretty prohibitive to issue quite possibly thousands of requests to Varnish when we need to update the cache.

In short: Is there a way to "ban" objects (based on URL regexes) while employing Grace Mode for all existing objects to ensure fast response times?


Solution

  • Unfortunately there is no soft ban function.

    The only thing I can give you is a soft purge function, that is part of vmod_purge, but it doesn't support regular expressions.

    See http://varnish-cache.org/docs/6.0/reference/vmod_generated.html#func-soft for more info. Although it's not exactly what you're looking for, it's better than nothing.

    Here's the VCL code:

    vcl 4.1;
    
    import purge;
    
    sub vcl_recv {
        if(req.method == "SOFTPURGE") {
            return(hash);
        }
    }
    
    sub vcl_hit {
        if(req.method == "SOFTPURGE") {
            purge.soft(0s,100s);
        }
    }