Search code examples
varnishvarnish-vclvarnish-4

Can Varnish 4 round_robin director be used to retry next server on 404


I know there are a lot of questions and answers on this topic. But most of them are for Varnish 3 or don't use the round_robin director.

  • I have 2 webservers configured in Varnish.
  • Some static files may only be generated at one of the two webservers.
  • On a 404 response, I want varnish to try the other webserver.

Currently i've tested with the following VCL:

sub vcl_init {
    new bar = directors.round_robin();
    bar.add_backend(cbweb1);
    bar.add_backend(cbweb2);
}

sub vcl_recv {
    set req.backend_hint = bar.backend();

    unset req.http.Cookie;
}

sub vcl_deliver {
    if(resp.status == 404 && req.restarts < 1)
    {
        return(restart);
    }
}

In my small test, this appears to work. But I don't understand how this works.

Does the bar.backend() remembers which servers have been used and skips those? Or is this just round-robinning globally, is it possible, a server would be called twice if another requests comes in during processing?

Update: The following VCL appears to work:

sub vcl_init {
    new bar = directors.round_robin();
    bar.add_backend(cbweb1);
    bar.add_backend(cbweb2);
}

sub vcl_recv {
    set req.backend_hint = bar.backend();
}

sub vcl_backend_fetch
{
    if (bereq.retries > 0)
    {
        if(bereq.http.X-Backend == "cbweb1")
        {
            set bereq.backend = cbweb2;
        }
        else
        {
             set bereq.backend = cbweb1;
        }
    }
}

sub vcl_backend_response {
    set bereq.http.X-Backend = beresp.backend.name;

    if (beresp.status == 404 && bereq.retries < 1) {
        return(retry);
    }
}

Solution

  • I think it works in your test because of round robin. It will try servers one after each other.

    I think in a production setup this won't work because of concurrent requests.

    If you retry, it will go again through the vcl_recv and get a backend_hint from the director, whatever is the req.restarts value. If you want a different backend then you will have to code it.

    An idea could be (code not tested, you will have to adapt the X-backend comparison):

    sub vcl_init {
        new bar = directors.round_robin();
        bar.add_backend(cbweb1);
        bar.add_backend(cbweb2);
    }
    
    sub vcl_recv {
        if (req.restarts > 0){
          if (req.X-backend == cbweb1 ){
            set req.backend_hint = cbweb2;
          }
          if (req.X-backend == cbweb2 ){
            set req.backend_hint = cbweb1;
          }
        }
        else {
          set req.backend_hint = bar.backend();
        }
        set req.X-backend = req.backend_hint
    
        unset req.http.Cookie;
    }
    
    sub vcl_deliver {
        if(resp.status == 404 && req.restarts < 1)
        {
            return(restart);
        }
    }