Serve stale content if backend is healthy but "not too healthy"
Carlos Abalde
carlos.abalde at gmail.com
Tue Sep 21 16:19:22 UTC 2021
Hi Luca,
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:
varnishtest "Full vs. limited grace"
server s_backend {
# First request when the cache is empty (see 1).
rxreq
txresp
# Background fetch done while inside the limited grace period (see 2).
rxreq
txresp
# Another request to refresh the content after the limited grace period has
# finished (see 3).
rxreq
txresp
# Another request to refresh the content after the limited grace period has
# finished one more time (see 4). This time, we return an error so a cached
# version will have to be returned to the client.
rxreq
txresp -status 500
} -start
varnish v_backend -vcl {
import std;
backend default {
.host = "${s_backend_addr}";
.port = "${s_backend_port}";
}
sub vcl_recv {
# Clean up internal headers.
if (req.restarts == 0) {
unset req.http.X-Varnish-Restarted-5xx;
}
unset req.http.X-Varnish-Use-Limited-Grace;
# Set a limited grace unless a restart has been done to use full grace.
if (!(req.restarts > 0 && req.http.X-Varnish-Restarted-5xx)) {
set req.http.X-Varnish-Use-Limited-Grace = "1";
set req.grace = 2s;
} else {
set req.grace = 100y;
}
}
sub vcl_backend_response {
set beresp.ttl = 1s;
# Set full grace value. This could be done by returning a proper value for
# the stale-while-revalidate property in the Cache-Control header, which
# Varnish understands (that's not the case with the stale-if-error
# property).
set beresp.grace = 24h;
# Send requests with a broken backend response to vcl_backend_error so they
# can be restarted.
if (beresp.status >= 500 && beresp.status < 600) {
return (error);
}
}
sub vcl_backend_error {
if (bereq.http.X-Varnish-Use-Limited-Grace && !bereq.uncacheable) {
# Trigger restart in the client side in order to enable full grace and
# try to deliver a staled object. Also, cache error response but under
# a variant to avoid overwritting the staled object that may already be
# in cache. Grace and keep are explicitly disabled to overwrite current
# default behaviour (https://github.com/varnishcache/varnish-cache/issues/3024).
set beresp.http.X-Varnish-Restart-5xx = "1";
set beresp.ttl = 1s;
set beresp.grace = 0s;
set beresp.keep = 0s;
set beresp.http.Vary = "X-Varnish-Use-Limited-Grace";
return (deliver);
} else {
# Jump to 'vcl_synth' with a 503 status code.
return (abandon);
}
}
sub vcl_deliver {
# Execute restart if the backend side requested so (see 'vcl_backend_error').
if (resp.http.X-Varnish-Restart-5xx && !req.http.X-Varnish-Restarted-5xx) {
set req.http.X-Varnish-Restarted-5xx = "1";
return (restart);
}
# Clean up Vary header.
if (resp.http.Vary == "X-Varnish-Use-Limited-Grace") {
unset resp.http.Vary;
}
# Debug.
set resp.http.X-Cache-Hits = obj.hits;
}
sub vcl_backend_fetch {
# Clean up internal headers.
if (bereq.retries == 0) {
unset bereq.http.X-Varnish-Restart-5xx;
}
# Do not retry requests restarted due to 5xx backend responses, no
# matter if a staled object has not been found or if a bgfetch has been
# spawned after serving staled content.
if (bereq.retries == 0 && bereq.http.X-Varnish-Restarted-5xx) {
# Jump to 'vcl_synth' with a 503 status code.
return (abandon);
}
}
} -start
client c1 -connect ${v_backend_sock} {
# 1: Ask for a content. This will hit the backend as the cache is empty.
txreq
rxresp
expect resp.status == 200
expect resp.http.X-Cache-Hits == 0
# Wait until the TTL is over.
delay 1.5
# 2: Further requests to the same content inside the limited grace period
# (set to 2 seconds) will be resolved by the cache. A bgfetch to the
# backend is silently made.
txreq
rxresp
expect resp.status == 200
expect resp.http.X-Cache-Hits == 1
# Wait until the new TTL and new limited grace period are over.
delay 5.0
# 3: Even if the content is in the cache (it's being stored for the full
# cache period: 24 hours), limited grace makes sure fresh content is
# recovered from the backend. A request to the content, therefore,
# produces a new hit in the backend.
txreq
rxresp
expect resp.status == 200
expect resp.http.X-Cache-Hits == 0
# Wait again until the new TTL and new limited grace period are over.
delay 3.5
# 4: A new request will try to get fresh content, but the backend now returns
# an error response, so Varnish restarts the request and serves stalled
# content. No background fetch is done as Varnish abort the attempt to
# disturb the failing backend.
txreq
rxresp
expect resp.status == 200
expect resp.http.X-Cache-Hits == 1
} -run
varnish v_backend -expect client_req == 4
Best,
--
Carlos Abalde
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.varnish-cache.org/lists/pipermail/varnish-misc/attachments/20210921/28dbe927/attachment-0001.html>
More information about the varnish-misc
mailing list