Reducing Video Startup Time using CMSD

Tags: vcl (29) video (1) ops (28)

Introduction

Video Startup Time (VST) is one of the key metrics used to evaluate the quality of experience (QoE) for video streaming services. It indicates the amount of time users have to wait before video playback starts after clicking play. This tutorial shows how to improve VST by ensuring that the objects required for video startup are always served directly from the edge cache for video-on-demand (VOD) workloads. These objects are called startup objects in this tutorial.

The method used is allocating separate stores to startup objects to avoid or reduce the amount of churn that happens to these objects. The result to expect is that all the startup objects are eventually served as cache hits from the edge, effectively optimizing VST for your entire VOD catalog, including medium and long tail content.

Churn and the effect on VST

Before the first frame is rendered, the video player downloads the initial manifests and video segments required. The faster the player downloads these objects, the faster it can start rendering the first frame and the lower (better) the VST will be.

With video-on-demand workloads, the dataset is often bigger than the cache capacity. In these environments, the least recently-used objects are continously evicted to make room for new objects. This is called churn. Churn will occur even for objects that have a long time-to-live (TTL) set, so it is not sufficient to put a longer TTL on the startup objects to avoid eviction from the cache.

The solution proposed to avoid or reduce churn for certain types of objects is to put them in one or more separate stores, preferably with a size that is sufficient to fit all of these objects.

Common Media Server Data (CMSD)

Common Media Server Data (CMSD) or CTA-5006 is a specification published by the Consumer Technology Association (CTA) in 2022. Its purpose is specified in the Introduction section:

“The purpose of the Common Media Server Data (CMSD) specification is to define a standard means by which every media server (intermediate and origin) can communicate data with each media object response and have it received and processed consistently by every intermediary and player, for the purpose of improving the efficiency and performance of distribution and ultimately the quality of experience enjoyed by the users."

The following example shows how the origin can indicate to intermediaries that a particular response is a startup object. It is done using the CMSD-Static response header with the reserved attribute su (start-up) set. Note that the header may contain multiple comma-separated attributes:

200 OK
CMSD-Static: su
Content-Type: video/mp4
Content-Length: 386700
Date: Tue, 05 Sep 2023 06:59:25 GMT
Server: Some origin

Prerequisites

To complete this guide, you’ll need:

  • A fully-functional video distribution environment based on Varnish Enterprise with Massive Storage Engine (MSE) enabled for persistent caching. Let’s assume the use of servers with four SSDs each for cache storage in the edge servers, but this number may be lower or higher.
  • A video origin that implements the CMSD specification or, at least, supports the key su (start-up) to the CMSD-Static response header.

Step 1 - Allocate a separate store for startup objects

The MSE configuration file /etc/varnish/mse.conf specifies the books and stores available on a specific Varnish server. From the list of stores on the server, one or more should be identified to use for startup objects. Be sure they have the same unique ID.

This shows a configuration file specifying four books and stores, where one store has the ID startup. The remaining three stores have the ID default. These are just custom strings, but they will be referenced in the VCL configuration later.

env: {
        id = "env";
        memcache_size = "auto";
        books = ({
                id = "disk1";
                directory = "/var/lib/mse/disk1/book";
                database_size = "20g";
                stores = ({
                        id = "startup";
                        filename = "/var/lib/mse/disk1/store.dat";
                        size = "6t";
                });
        }, {
                id = "disk2";
                directory = "/var/lib/mse/disk2/book";
                database_size = "20g";
                stores = ({
                        id = "default";
                        filename = "/var/lib/mse/disk2/store.dat";
                        size = "6t";
                });
        }, {
                id = "disk3";
                directory = "/var/lib/mse/disk3/book";
                database_size = "20g";
                stores = ({
                        id = "default";
                        filename = "/var/lib/mse/disk3/store.dat";
                        size = "6t";
                });
        }, {
                id = "disk4";
                directory = "/var/lib/mse/disk4/book";
                database_size = "20g";
                stores = ({
                        id = "default";
                        filename = "/var/lib/mse/disk4/store.dat";
                        size = "6t";
                });
        });
};
If this configuration file is changed, you may need to rebuild the books and stores using the command below. If the file is not changed, you can skip the following command and avoid rebuilding the cache.

sudo mkfs.mse -f -c /etc/varnish/mse.conf

Step 2 - Identify startup objects

The functionality to work with request and response headers is provided by vmod-headerplus. The following VCL uses the function headerplus.attr_exists() to identify startup objects coming from the backend by checking if the su attribute exists:

vcl 4.1;

import headerplus;
sub vcl_backend_response {
    if (headerplus.attr_exists("CMSD-Static", "su")) {
        # This backend response is a startup object.
    } else {
        # This backend response is NOT a startup object.
    }
}
The headerplus.attr_exists() function call was introduced in Varnish Enterprise 6.0.11r5.

Step 3 - Put startup objects in a separate store

The functionality to influence storage management per transaction is provided by vmod-mse. The following expands on the VCL above to put startup objects in a specific store allocated to startup objects:

vcl 4.1;

import mse;
import headerplus;
sub vcl_backend_response {
    if (headerplus.attr_exists("CMSD-Static", "su")) {
        # This backend response is a startup object.
        mse.set_stores("startup");
    } else {
        # This backend response is NOT a startup object.
        mse.set_stores("default");
    }

    # If multiple stores have the same tag, then Varnish will balance the
    # cache inserts across the stores matching the tag. We set the weighting
    # to smooth for better balancing across the stores.
    mse.set_weighting(smooth);
}

Conclusion

You should now have reduced the video startup time for your entire VOD catalog by allocating a portion of your storage capacity to certain objects that directly impact the QoE. You’ve ensured that these important startup objects are never, or rarely, evicted from the cache before they expire. The result should be most noticeable for medium and long tail content.

Terminology

Expiration: An object’s TTL is reached and it’s no longer considered a fresh object. Varnish will try to revalidate or fetch the object again from the origin before serving it to a client.

Eviction: When Varnish removes a fresh object (still within its TTL period) to free up cache capacity to be able to insert new objects into the cache

Churn: When eviction happens on a regular basis and it indicates that the active dataset is bigger than the cache capacity. This is not unusual for many workloads, including VOD.

Additional reading