Search

Object lifetime: TTL, Grace, Keep Tutorial

Introduction

If you landed on this page it is very likely that you are familiar with Varnish and you know it improves content delivery performance by storing a copy of your content in cache, and every request thereafter is fulfilled by the cached content. Every copy of the content (aka object) stored in cache has a lifetime which defines how long an object can be considered fresh, or live, within the cache.

World

The lifetime of a cached object is represented by the above timeline. The life of an object starts at the t_origin which is the time when the object was inserted in cache.

On top of this Timestamp we have three duration attributes:

  • 1. TTL - Time To Live
  • 2. Grace
  • 3. keep

An object lives in cache until TTL + Grace + Keep elapses, after that time the object is removed by the Varnish daemon. Objects within the TTL are considered fresh objects, while stale objects are those whose lifetime is between TTL and Grace, this is called grace period. Object lifetime between t_origin and keep is used for conditional requests using the HTTP Header field If-Modified-Since.

Setting TTLs, Grace and Keep

Setting the right lifetime durations is fundamental for a healthy cache, avoid the waste of resources such as cache storage and make sure the users have a great quality of experience.

This is how you can set TTLs, grace and keep:

TTL Grace Keep
VCL Yes Yes Yes
CLI Yes Yes Yes
HTTP headers Yes Yes Yes

It’s important to remember a Varnish object is a local store of a HTTP response message, therefore TTL, Grace and Keep can be set via HTTP headers either by the origin servers or by Varnish itself.

Before diving in and understand lifetime settings using examples, let’s clarify how Varnish handles TTLs/Grace/Keep. Varnish, when fetching the content to be cached, will first check if any TTL-related header has been set by the origin server. If it has been set by the origin server, then Varnish will honor it, but we could still change it either via VCL or Varnish parameters if we need to apply a modification. If no TTLs have been set by the origin server, then Varnish will apply its own default TTLs.

Note: from here on when we refer to TTLs we will refer to one of the three lifetime durations, while we will use TTL (in the singular) version when we refer specifically to Time To Live.


1. Set via VCL

The flexibility of VCL gives us the freedom to set, rewrite, adjust any header/lifetime we need/want.

The right place to set TTLs is the vcl_backend_response function, that’s the subroutine where the origin server response is processed by Varnish and that’s where objects attributes can be changed before the content is stored in cache. Once an object is in cache it can’t be modified anymore.

An example for video content delivery:

sub vcl_backend_response {

	# We first set TTLs valid for most of the content we need to cache
	set beresp.ttl = 10m;
	set beresp.grace = 2h;

	# We can now set specific TTLs based on the content we need to cache
	# For VoD content we set a medium-long TTL and a long grace as the VoD
	# content is very unlikley to change. This allow us to use the cache
	# for the most-requested content

	if (beresp.url ~ "/vod") {
		set beresp.ttl = 30m;
		set beresp.grace = 24h;
	}

	# For live content we use a very small TTL and a even smaller grace period
	# that's because live content is no longer *live* once it is consumed
	
	if (beresp.url ~ "/url") {
		set beresp.ttl = 10s;
		set beresp.grace = 2s;
	}
	
	# We expand the *keep* duration for IMS

	if (bereq.http.If-Modified-Since) {
		set beresp.keep = 10m;
	}
}	

An example for e-commerce and web&API:

sub vcl_backend_response {

        # We first set TTLs for most of the content we need to cache

        set beresp.ttl = 2h;
        set beresp.grace = 24h;

        # For static content we set a long TTL and grace as it is
        # unlikely to change over time
        
        if (beresp.url ~ "\.(png|gif|jpg|swf|css|js)$") {
                set beresp.ttl = 1w;
                set beresp.grace = 24h;
        }

        # Dynamic content can either change over time, or it can be personalized
        # for each user, or, more in generel, it can change under specific conditions
        
        if (beresp.url ~ "/user_personalization") {
                set beresp.ttl = 30m;
                set beresp.grace = 10m;
        }
	
        if (beresp.url ~ "/time_based") {
               	set beresp.ttl = 30m;
                set beresp.ttl = 10m;
                set beresp.keep = 5m;
        }
}

2. Set via CLI

VCL is by far the most flexible method we have for setting these TTLs, but we can also rely on the CLI or on startup parameters to set default TTLs.

varnishadm param.show returns the parameters the Varnish daemon is using at run time, among those you can find:

default_grace                 10.000 [seconds] (default)
default_keep                  0.000 [seconds] (default)
default_ttl                   120.000 [seconds] (default)

If TTLs are not set by the origin server or defined via VCL, Varnish will apply the default values: a TTL of 2 minutes and a Grace of 10 seconds. These default values are supposed to work fine for most of the Varnish setups, but the best practice is always to make sure you adjust the TTLs durations to your consumers and business specific needs.

The default values can be changed temporarily or persistently.

* Temporarily - via varnishadm:

Before applying permament changes, it’s advisable to test them and carefully roll them in production. Setting paramters using varnishadm is great for testing as you can set values which won’t survive over restarts.

varnishadm param.set <param> <value> will do the trick, i.e. varnishadm param.set default_ttl 1.000 will set the default TTL(Time To Live) to 1 second.

Furthermore, using varnishadm param.reset <param> you can quickly set the parameters to their default values without having an impact on Varnish performances.

* Persistently - as startup parameters:

Once you have completed your tests using varnishadm and you are satisfied with the results you can think of setting the new TTLs values as startup parameters, to do so the unite varnish.service file must be edited as follwing:

sudo systemctl edit --full varnish.service

This will open the already existing varnish.service file allowing you to enter modifications for the service. Below is an example, adding a default_ttl of 1 second to the defaults:

[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd -a :6081 -f /etc/varnish/default.vcl -s malloc,256m -p default_ttl=1

Save the file and restart Varnish to apply permanent changes:

sudo systemctl restart varnish.service

3. Set via HTTP headers

As mentioned in the introduction, a HTTP caching strategy should start from the origin server(s), where the content is created, therefore it is really important to understand that we have a full set of HTTP headers we can use to define TTLs.

The most commonly used are:

  • Cache-Control
  • Expires
  • Age
  • Etag
  • Last-Modified
  • If-Modified-Since
  • If-None-Match

  • Cache-Control header specifies directives which must be respected by all caching mechanisms. Cache-Control: public, max-age=36000 means the response can be cached by any cache and it will be considered fresh for 36000 seconds Cache control has many directives which can be request or response specific. Check the RFC for more details.

RFC: https://tools.ietf.org/html/rfc7234#section-5.2

  • The Expires header field gives the date/time after which the response is considered stale. For example Expires: Thu, 01 Dec 2020 16:00:00 GMT

  • The Age header field conveys the sender’s estimate of the amount of time since the response was generated or successfully validated at the origin server.

RFC: https://tools.ietf.org/html/rfc7234#section-5.1

  • Etag, Last-Modified, If-Modified-Since, If-None-Match these are conditional headers and must be used when sending cache validation request. The scope of those headers is to indicate if a cache has the most-up-to date version of the cached content or if fresher content can be server by the origin server. In example, if a client send a GET or a HEAD request with a If-Modified-Since: Tue, 29 Oct 2019 19:43:31 GMT, Varnish will revalidate the available content in cache by sending a conditional request to the origin server to check if the request content has been modified after Sat, 29 Oct 2019 19:43:31 GMT

RFC: https://tools.ietf.org/html/rfc7232#section-3

Setting HTTP headers at the origin server is advisable, but if somehow those headers are not defined when the content is created you can always rely on VCL as explain at point 1.