February 8, 2022
4 min read time

Two-Minute Tech Tuesdays - prefetching

2MTT prefetching

In this week’s episode of Two-Minute Tech Tuesday, we'll talk about prefetching, a concept of proactively fetching content from the server that wasn't requested by the client. This ensures a good hit rate when the client eventually requests the prefetched content.

 

 

Prefetching is usually instructed by the application that has prior knowledge of the required resources. It's initiated through a Link response header or a Link HTML tag.

In the case of Varnish, content can be stored in the cache before the content is requested by the client. This improves the hit rate, speed, and user experience.

Imagine that a client sends a request for the homepage of a website to Varnish. Varnish will:

  1. Fetch the request from the origin
  2. Store the output in the cache
  3. Inspect the output for a potential "Link" header that does prefetching

Screen Shot 2022-02-07 at 9.40.54 PM

In this case, we'll prefetch main.css. As we deliver the requested output to the client, we'll perform an internal subrequest for the main.css file. When Varnish doesn't have it stored in the cache, it will fetch that content from the origin and then store it for future use.

 

VMOD_HTTP

Prefetching can be done using vmod_http, which is a Varnish Enterprise module that performs HTTP calls in VCL. This is what the VCL code looks like:

 

vcl 4.1;

import http;

backend default {
.host="127.0.0.1";
.port="8080";
}

sub vcl_recv {
set req.http.X-prefetch = http.varnish_url("/");
}

sub vcl_backend_response {
if(beresp.http.Link ~ "<([^>]+)>; rel=(prefetch|next)") {
set bereq.http.X-link = regsub(beresp.http.Link, "^.*<([^>]*)>.*$", "\1");
set bereq.http.X-prefetch = regsub(bereq.http.X-prefetch, "/$", bereq.http.X-link);
http.init(0);
http.req_copy_headers(0);
http.req_set_url(0, bereq.http.X-prefetch);
http.req_send_and_finish(0);
}
}

 

We start with importing the VMOD, then setting the URL of Varnish for the internal subrequest by leveraging the http.varnish_url() function. As the backend responds with the output, we'll check whether or not it contains a Link header matching the prefetching pattern.

If it does, we'll extract the URL from the header and store it in X-Link. We can then use this to populate the URL with the actual URL we need for the internal subrequest.

Now that we have a URL, we can perform an HTTP call. Start by initializing the HTTP client copy in the request headers and injecting the URL. Through req_send_and_finish, we perform a "fire and forget" request that asynchronously sends an internal subrequest to the origin without blocking this transaction.

 

Other prefetching use cases

Prefetching can also happen within an online video context where OTT video platforms also leverage HTTP. Imagine that a video player uses the HLS protocol and loads an "m3u8" playlist file. This playlist contains all the streams that need to be loaded to create that online video streaming experience.

Because these streams are loaded sequentially, it's quite easy to guess what the URL of the next stream will be. That's how we can perform prefetching while requesting one segment and simultaneously figuring out what the next one will be. See the VCL code:

 

vcl 4.1;

import http;

backend default {
.host="127.0.0.1";
.port="8080";
}

sub vcl_recv {
if (req.url ~ "^/vod/stream_[0-9]+\.ts$") {
http.init(0);
http.req_copy_headers(0);
http.req_set_url(0, http.prefetch_next_url());
http.req_send_and_finish(0);
}
}

 

Again, we will start off by importing the VMOD. Then we intercept requests where the URL matches our streaming pattern.

If it does, we'll initialize the HTTP client and use the http.prefetch_next_url() function to guess what the next URL will be. We'll then perform a "fire and forget" with the intent that the content will eventually be stored in the cache.

Prefetching is not only there to protect the origin. Its main goal is to improve the quality of experience for the user and make sure that the content it will need later, is already stored in the cache.

varnish_6_by_example_download