Stale object API. This API allows for a safe and high performance Stale-If-Error implementation.
Note: Using a Stale-If-Error implementation that depends on abandoning backend fetches and restarting is unsafe for sites which serve moderate to high levels of traffic. This is because the abandon-restart loop can cause significant amounts of request serialization since Varnish will only allow a single request per object to hit the abandon-restart loop at a time.
Note: A bug in vmod_stale version 6.0.6r3 can cause it to function unsafely on hit-for-miss objects. This has been resolved in versions 6.0.6r4 and later.
First, make sure a suitable beresp.keep
value is set. This value determines the Stale-If-Error period.
beresp.ttl
and beresp.grace
do not need to be changed. Remember, the lifetime of an object is
ttl + grace + keep
.
sub vcl_backend_response {
# Keep these values unchanged
set beresp.ttl = ...;
set beresp.grace = ...;
# Add a 7 day Stale-If-Error period
set beresp.keep = 7d;
}
Or you can use the following VCL to automatically read the stale-if-error
value from the
Cache-Control
header and use it. If no value is found, Stale-If-Error is disabled for the object.
import headerplus;
import std;
sub vcl_backend_response {
# Automatically set the stale-if-error value (keep) from Cache-Control
headerplus.init(beresp);
set beresp.keep = std.duration(
headerplus.attr_get("Cache-Control", "stale-if-error") + "s",
0s); # This is the default value if not found
}
Next, put the following VCL at the top of your sub vcl_backend_*
section.
import stale;
sub stale_if_error {
if (beresp.status >= 500 && stale.exists()) {
# Tune this value to match your traffic and caching patterns
stale.revive(20m, 1h);
stale.deliver();
return (abandon);
}
}
sub vcl_backend_response {
call stale_if_error;
}
sub vcl_backend_error {
call stale_if_error;
}
Make note of the stale.revive(TTL, grace)
call above. The values used should be large enough to
limit backend traffic and prevent a thundering herd problem when the backend comes back online.
It also needs to be small enough to allow Varnish to be responsive to when your backend does come
back online.
Using beresp.keep
is entirely optional, beresp.grace
can also be re-leveraged for Stale-If-Error.
If using grace, the client will immediately get a stale object instead of waiting for an error to trigger
the stale object delivery. If disabling grace via req.grace
, the grace behavior will match Stale-If-Error.
In all cases, if a stale object exists, no fresh error response will reach the client.
The following 2 varnishstat
counters can be used to monitor stale activity:
MAIN.fetch_stale_deliver
MAIN.fetch_stale_rearm
Note: The following API only works in a backend context (sub vcl_backend_*
).
Using the API outside of a backend context will result in a request failure.
BOOL exists()
Does a stale object exist?
Arguments
None
Returns
true
if a stale object exists, otherwise false
.
BOOL deliver()
Deliver the stale object to the client. Must be used with return(abandon)
.
If normal return(deliver)
is done, the stale object will not be returned.
If the request is a bgfetch or there is no client waiting, this is a no-op.
A successful stale delivery will increment the following varnishstat
counter:
MAIN.fetch_stale_deliver
Arguments
None
Returns
true
if the stale object can be delivered, otherwise false
.
A bgfetch will return false
.
VOID revive(DURATION ttl, DURATION grace)
Revive the stale object with a new ttl
and grace
. The original time of expiration is maintained.
The object will not revive beyond its initial beresp.ttl + beresp.grace + beresp.keep
. The leftover
time to expiration is stored back as the keep
value.
Reviving an object will increment the MAIN.fetch_stale_rearm
varnishstat counter.
Arguments
DURATION ttl
- the new TTL value. If less than or equal to 0s
, the revive call does nothing.
DURATION grace
- the new grace value. If less than 0s
, the value is rounded upto 0s
.
Returns
Nothing
VOID rearm(DURATION ttl, DURATION grace, DURATION keep)
Set a new ttl
, grace
, and keep
. These values overwrite any existing values and
can extend the time of expiration to any point in time. A value of 0s
clears the value out.
Negative values keep the existing value in place.
Rearming an object will increment the MAIN.fetch_stale_rearm
varnishstat counter.
Arguments
DURATION ttl
- the new TTL value. A negative value keeps the previous expired TTL value.
DURATION grace
- the new grace value. A negative value keeps the previous grace value.
DURATION keep
- the new keep value. A negative value keeps the previous keep value.
Returns
Nothing
INT get_status()
Get the response status code from the stale object.
Arguments
None
Returns
The response status code from the stale object. 0
if the stale object does not exist.
STRING get_header(STRING name, STRING default)
Get a response header from the stale object.
Arguments
STRING name
- The header name.
STRING default
optional
- The value to return if the header is not found
or the stale object does not exist. Defaults to an empty false value.
Returns
The header value if it exists, otherwise default
.
DURATION get_ttl(BOOL remaining = true)
Get the TTL value from the stale object.
Arguments
BOOL remaining
optional
- Get the remaining TTL value. Defaults to true
.Returns
The TTL value from the stale object. When remaining
is true
and the object is expired,
this value is negative.
DURATION get_grace(BOOL remaining = false)
Get the grace value from the stale object.
Arguments
BOOL remaining
optional
- Get the remaining grace value. Defaults to false
.Returns
The grace value from the stale object. When remaining
is true
and the object is past grace,
this value is negative.
DURATION get_keep(BOOL remaining = false)
Get the keep value from the stale object.
Arguments
BOOL remaining
optional
- Get the remaining keep value. Defaults to false
.Returns
The keep value from the stale object. When remaining
is true
, this value is the total time to
expiration of the stale object.