<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class="">Hi Luca,</div><div class=""><br class=""></div><div class="">As Dridi and Guillaume said, what you're looking for is something like the stale VMOD. I think the OSS approach suggested by Guillaume could be extended a little bit in order to improve performance and avoid request serialization. Problem is the final VCL is *ugly* and hard to understand. In any case, next I'm sharing a VTC showing how it works:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">varnishtest "Full vs. limited grace"</div><div class=""><br class=""></div><div class="">server s_backend {</div><div class="">    # First request when the cache is empty (see 1).</div><div class="">    rxreq</div><div class="">    txresp</div><div class=""><br class=""></div><div class="">    # Background fetch done while inside the limited grace period (see 2).</div><div class="">    rxreq</div><div class="">    txresp</div><div class=""><br class=""></div><div class="">    # Another request to refresh the content after the limited grace period has</div><div class="">    # finished (see 3).</div><div class="">    rxreq</div><div class="">    txresp</div><div class=""><br class=""></div><div class="">    # Another request to refresh the content after the limited grace period has</div><div class="">    # finished one more time (see 4). This time, we return an error so a cached</div><div class="">    # version will have to be returned to the client.</div><div class="">    rxreq</div><div class="">    txresp -status 500</div><div class="">} -start</div><div class=""><br class=""></div><div class="">varnish v_backend -vcl {</div><div class="">    import std;</div><div class=""><br class=""></div><div class="">    backend default {</div><div class="">        .host = "${s_backend_addr}";</div><div class="">        .port = "${s_backend_port}";</div><div class="">    }</div><div class=""><br class=""></div><div class="">    sub vcl_recv {</div><div class="">        # Clean up internal headers.</div><div class="">        if (req.restarts == 0) {</div><div class="">            unset req.http.X-Varnish-Restarted-5xx;</div><div class="">        }</div><div class="">        unset req.http.X-Varnish-Use-Limited-Grace;</div><div class=""><br class=""></div><div class="">        # Set a limited grace unless a restart has been done to use full grace.</div><div class="">        if (!(req.restarts > 0 && req.http.X-Varnish-Restarted-5xx)) {</div><div class="">            set req.http.X-Varnish-Use-Limited-Grace = "1";</div><div class="">            set req.grace = 2s;</div><div class="">        } else {</div><div class="">            set req.grace = 100y;</div><div class="">        }</div><div class="">    }</div><div class=""><br class=""></div><div class="">    sub vcl_backend_response {</div><div class="">        set beresp.ttl = 1s;</div><div class=""><br class=""></div><div class="">        # Set full grace value. This could be done by returning a proper value for</div><div class="">        # the stale-while-revalidate property in the Cache-Control header, which</div><div class="">        # Varnish understands (that's not the case with the stale-if-error</div><div class="">        # property).</div><div class="">        set beresp.grace = 24h;</div><div class=""><br class=""></div><div class="">        # Send requests with a broken backend response to vcl_backend_error so they</div><div class="">        # can be restarted.</div><div class="">        if (beresp.status >= 500 && beresp.status < 600) {</div><div class="">            return (error);</div><div class="">        }</div><div class="">    }</div><div class=""><br class=""></div><div class="">    sub vcl_backend_error {</div><div class="">        if (bereq.http.X-Varnish-Use-Limited-Grace && !bereq.uncacheable) {</div><div class="">            # Trigger restart in the client side in order to enable full grace and</div><div class="">            # try to deliver a staled object. Also, cache error response but under</div><div class="">            # a variant to avoid overwritting the staled object that may already be</div><div class="">            # in cache. Grace and keep are explicitly disabled to overwrite current</div><div class="">            # default behaviour (<a href="https://github.com/varnishcache/varnish-cache/issues/3024" class="">https://github.com/varnishcache/varnish-cache/issues/3024</a>).</div><div class="">            set beresp.http.X-Varnish-Restart-5xx = "1";</div><div class="">            set beresp.ttl = 1s;</div><div class="">            set beresp.grace = 0s;</div><div class="">            set beresp.keep = 0s;</div><div class="">            set beresp.http.Vary = "X-Varnish-Use-Limited-Grace";</div><div class="">            return (deliver);</div><div class="">        } else {</div><div class="">            # Jump to 'vcl_synth' with a 503 status code.</div><div class="">            return (abandon);</div><div class="">        }</div><div class="">    }</div><div class=""><br class=""></div><div class="">    sub vcl_deliver {</div><div class="">        # Execute restart if the backend side requested so (see 'vcl_backend_error').</div><div class="">        if (resp.http.X-Varnish-Restart-5xx && !req.http.X-Varnish-Restarted-5xx) {</div><div class="">            set req.http.X-Varnish-Restarted-5xx = "1";</div><div class="">            return (restart);</div><div class="">        }</div><div class=""><br class=""></div><div class="">        # Clean up Vary header.</div><div class="">        if (resp.http.Vary == "X-Varnish-Use-Limited-Grace") {</div><div class="">            unset resp.http.Vary;</div><div class="">        }</div><div class=""><br class=""></div><div class="">        # Debug.</div><div class="">        set resp.http.X-Cache-Hits = obj.hits;</div><div class="">    }</div><div class=""><br class=""></div><div class="">    sub vcl_backend_fetch {</div><div class="">        # Clean up internal headers.</div><div class="">        if (bereq.retries == 0) {</div><div class="">            unset bereq.http.X-Varnish-Restart-5xx;</div><div class="">        }</div><div class=""><br class=""></div><div class="">        # Do not retry requests restarted due to 5xx backend responses, no</div><div class="">        # matter if a staled object has not been found or if a bgfetch has been</div><div class="">        # spawned after serving staled content.</div><div class="">        if (bereq.retries == 0 && bereq.http.X-Varnish-Restarted-5xx)  {</div><div class="">            # Jump to 'vcl_synth' with a 503 status code.</div><div class="">            return (abandon);</div><div class="">        }</div><div class="">    }</div><div class="">} -start</div><div class=""><br class=""></div><div class="">client c1 -connect ${v_backend_sock} {</div><div class="">    # 1: Ask for a content. This will hit the backend as the cache is empty.</div><div class="">    txreq</div><div class="">    rxresp</div><div class="">    expect resp.status == 200</div><div class="">    expect resp.http.X-Cache-Hits == 0</div><div class=""><br class=""></div><div class="">    # Wait until the TTL is over.</div><div class="">    delay 1.5</div><div class=""><br class=""></div><div class="">    # 2: Further requests to the same content inside the limited grace period</div><div class="">    # (set to 2 seconds) will be resolved by the cache. A bgfetch to the</div><div class="">    # backend is silently made.</div><div class="">    txreq</div><div class="">    rxresp</div><div class="">    expect resp.status == 200</div><div class="">    expect resp.http.X-Cache-Hits == 1</div><div class=""><br class=""></div><div class="">    # Wait until the new TTL and new limited grace period are over.</div><div class="">    delay 5.0</div><div class=""><br class=""></div><div class="">    # 3: Even if the content is in the cache (it's being stored for the full</div><div class="">    # cache period: 24 hours), limited grace makes sure fresh content is</div><div class="">    # recovered from the backend. A request to the content, therefore,</div><div class="">    # produces a new hit in the backend.</div><div class="">    txreq</div><div class="">    rxresp</div><div class="">    expect resp.status == 200</div><div class="">    expect resp.http.X-Cache-Hits == 0</div><div class=""><br class=""></div><div class="">    # Wait again until the new TTL and new limited grace period are over.</div><div class="">    delay 3.5</div><div class=""><br class=""></div><div class="">    # 4: A new request will try to get fresh content, but the backend now returns</div><div class="">    # an error response, so Varnish restarts the request and serves stalled</div><div class="">    # content. No background fetch is done as Varnish abort the attempt to</div><div class="">    # disturb the failing backend.</div><div class="">    txreq</div><div class="">    rxresp</div><div class="">    expect resp.status == 200</div><div class="">    expect resp.http.X-Cache-Hits == 1</div><div class="">} -run</div><div class=""><br class=""></div><div class="">varnish v_backend -expect client_req == 4</div></blockquote><div class=""><br class=""></div><div class="">Best,</div><div class=""><br class=""></div><div class="">--</div><div class="">Carlos Abalde</div><div class=""><br class=""></div></body></html>