Stale object API.
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. This API allows for a safe and high performance Stale-If-Error implementation and does not depend on abandoning backend fetches.
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
The stale API only operates 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
Type: Function
Returns: Bool
Restricted to: backend
BOOL deliver()
Deliver the stale object to the client. Must be used with return(abandon)
.
Failing to do return (abandon)
after calling .deliver()
will cause the fetched object (typically an error object) to be inserted and the stale object to be replaced.
This typically is exactly the opposite of what you want calling .deliver()
.
A successful stale delivery will increment the following varnishstat
counter:
MAIN.fetch_stale_deliver
Arguments: None
Type: Function
Returns: Bool
Restricted to: backend
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:
ttl
accepts type DURATION
grace
accepts type DURATION
Type: Function
Returns: None
Restricted to: backend
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:
ttl
accepts type DURATION
grace
accepts type DURATION
keep
accepts type DURATION
Type: Function
Returns: None
Restricted to: backend
INT get_status()
Get the status code of the stale object (0 if it doesn’t exist).
Arguments: None
Type: Function
Returns: Int
Restricted to: backend
STRING get_header(STRING name, STRING default = 0)
Get the header value (default if it the header or stale object doesn’t exist).
Arguments:
name
accepts type STRING
default
accepts type STRING with a default value of 0
optional
Type: Function
Returns: String
Restricted to: backend
DURATION get_ttl(BOOL remaining = 1)
Get the TTL value from the stale object. Using remaining gets the remaining TTL time for the stale object.
Arguments:
remaining
accepts type BOOL with a default value of 1
optional
Type: Function
Returns: Duration
Restricted to: backend
DURATION get_grace(BOOL remaining = 0)
Get the grace value from the stale object. Using remaining gets the remaining grace time for the stale object.
Arguments:
remaining
accepts type BOOL with a default value of 0
optional
Type: Function
Returns: Duration
Restricted to: backend
DURATION get_keep(BOOL remaining = 0)
Get the keep value from the stale object. Using remaining gets the overall time to expiration of the stale object.
Arguments:
remaining
accepts type BOOL with a default value of 0
optional
Type: Function
Returns: Duration
Restricted to: backend
INT get_hits()
Get the number of hits on the stale object (0 if it doesn’t exist).
Arguments: None
Type: Function
Returns: Int
Restricted to: backend
The stale
VMOD is available in Varnish Enterprise version 6.0.6r3
and later.