2

I'm using stale-if-error to deliver stale content while the server is marked unhealthy looking at grace and keep object options in vcl_hit.

The question is: Is it possible to deliver a cache object after entering the vcl subroutine vcl_backend_error (fresh request error). Actually, I deliver cached object at vcl_hit but looking at the next diagram, I don't see how it is possible to access the cached object of that request.

https://www.varnish-software.com/book/4.0/chapters/VCL_Basics.html

Source: https://www.varnish-software.com/book/4.0/chapters/VCL_Basics.html

Ray Jennings
  • 336
  • 2
  • 10
Wiliam
  • 3,714
  • 7
  • 36
  • 56
  • I do not quite understand your question. As far as I understand in Varnish 4 when a new request arrives after time-to-live of the cached object but within grace time Varnish will deliver the stale object and create an async request to the backend. If this request fails the stale object will still be delivered till the end of grace time. – Ronald Oct 05 '15 at 17:21

1 Answers1

3

When using the built-in VCL (see code bellow):

# Built-in 'vcl_hit'.
sub vcl_hit {
    if (obj.ttl >= 0s) {
        return (deliver);
    }

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

    return (fetch);
}
  • If vcl_backend_error is reached by a background / asynchronous backend fetch triggered by a return (deliver) during vcl_hit you don't need to worry. It's just a background thread to update a stalled object. The stalled content has already been delivered to the client.

  • If vcl_backend_error is reached by a synchronous backend fetch triggered by a return (fetch) during vcl_hit you don't need to worry too. An error will be delivered to the client, but you have not choice. A stalled object is not available in the Varnish storage.

However, if you have customised vcl_hit to limit grace when the backend is healthy (see VCL example code below), a return (fetch) executed during vcl_hit will be handled as a synchronous backend request. The client will wait for the backend response. If the backend request reaches vcl_backend_error and error will be delivered to the client side. A stalled object is available in the Varnish storage (stalled more than 60 seconds ago in this example), but it's not going to be used.

# Customised 'vcl_hit'.
sub vcl_hit {
    if (obj.ttl >= 0s) {
        return (deliver);
    }

    if (std.healthy(req.backend_hint)) {
        if (obj.ttl + 60s > 0s) {
            return (deliver);
        }
    } else {
        if (obj.ttl + obj.grace > 0s) {
            return (deliver);
        }
    }

    return (fetch);
}

If you want to deliver stalled objects when the synchronous backend fetch fails, in this case you need some extra VCL logic. The idea is shown in the code below:

backend fail_be {
    .host = "127.0.0.1";
    .port = "9000";
    .probe = {
        .url = "/give-me-a-non-200-please";
        .interval = 24h;
        .timeout = 1s;
        .window = 1;
        .threshold = 1;
    }
}

sub vcl_recv {
    # Force the non-healthy backend in case of restart because of a previous
    # failed backend fetch. This will force serving stalled content using
    # full grace during 'vcl_hit' (if possible).
    if (req.restarts == 0) {
        unset req.http.X-Varnish-Restarted-5xx;
    } else {
        if (req.http.X-Varnish-Restarted-5xx) {
            set req.backend_hint = fail_be;
        }
    }

    # ...
}

sub vcl_synth {
    # 503 generated for synchronous client requests when abandoning the
    # backend request (see 'vcl_backend_fetch') and not executing a POST.
    if (resp.status == 503 &&
        req.method != "POST" &&
        !req.http.X-Varnish-Restarted-5xx) {
        set req.http.X-Varnish-Restarted-5xx = "1";
        return (restart);
    }

    # ...
}

sub vcl_backend_fetch {
    if (bereq.retries == 0) {
        unset bereq.http.X-Varnish-Backend-5xx;
    } else {
        if (bereq.http.X-Varnish-Backend-5xx) {
            # Jump to 'vcl_synth' with a 503 status code.
            return (abandon);
        }
    }

    # ...
}

sub vcl_backend_response {
    if (beresp.status >= 500 && beresp.status < 600) {
        set bereq.http.X-Varnish-Backend-5xx = "1";
        return (retry);
    }

    # ...
}

sub vcl_backend_error {
    set bereq.http.X-Varnish-Backend-5xx = "1";
    return (retry);
}
Carlos Abalde
  • 1,077
  • 7
  • 12
  • This is too much logic but your answer is good. Thanks. I will wait for some more answers and if any is more simple, I will check yours. I'm populating "grace" with backend response stale-while-revalidate and "keep" with stale-if-error and I was trying to use stale-if-error if the backend returns an error in a synchronous request, because the cache object exists (because the keep value). – Wiliam Oct 06 '15 at 21:08
  • 2
    About simplicity, if you prefer, the VCL logic can be simplified a little bit: (1) the always-sick backend (`fail_be`) is there to keep the logic more readable, but is not required (you can remove it and move the `req.restarts > 0 && req.http.X-Varnish-Restarted-5xx` to `vcl_hit`); and (2) the logic in `vcl_backend_.*` subroutines can be highly simplified when using Varnish Cache 4.1 (i.e. remove `vcl_backend_fetch` and simply `return (abandon)` in `vcl_backend_response` and `vcl_backend_error` instead of `return (retry)`). – Carlos Abalde Oct 07 '15 at 06:58