Faster than before



Introduction

HTTP Server Push allows an server to push an asset to the client before it's ever requested by them, reducing the latency for those objects. Server push is supported natively by Fastly. Furthermore, unlike traditional Link: headers in a browser setting, server push can be initiated by VCL, meaning a push can be started before the original request is even finished by the user.

There are a lot of discussions about the usability of server push in generalized settings, when it should or should not be used, etc. But for the case of the Nix cache, server push logic is nearly ideal and very simple: when a client requests a .narinfo file, they look at the contents to find the actual .nar to download. However, this file can be pushed by the server immediately upon the first request for the .narinfo. Furthermore, the client can specifically cooperate with the server to help guide server push. For instance, when nix copy downloads from an HTTP server, it can send a header requesting server push for the .narinfo. Fastly VCL can specifically look for this header, and only trigger under the condition that a .narinfo request has that header. This means push will not occur in "undesirable" settings for other HTTP clients hitting the cache.

This is not to say server push is a clear win for Nix users. It might be a win. Rather, server push is relatively easy to support at the edge level, and likely worth experimenting with, modulo a few considerations...


Step 1: Enhance nix copy for .narinfo files

The first step is to enhance nix copy so that when it uploads a .narinfo file to S3, it also tags that object with S3 metadata. Quoting the S3 documentation:

When uploading an object, you can also assign metadata to the object. You provide this optional information as a name-value (key-value) pair when you send a PUT or POST request to create the object. When you upload objects using the REST API, the optional user-defined metadata names must begin with "x-amz-meta-" to distinguish them from other HTTP headers. When you retrieve the object using the REST API, this prefix is returned...

For instance, when uploading 003plaambir851ybq8jg6jqnaqwb76a3.narinfo into the cache, which has the following contents:

StorePath: /nix/store/003plaambir851ybq8jg6jqnaqwb76a3-fuse-2.9.8
URL: nar/0fsswq296aczj16q6harvnx0999bm1nspi0vcyrppj0qcbngky00.nar.xz
Compression: xz
...

Then we would also upload a piece of metadata along with the .narinfo, for instance:

x-amz-meta-Link: nar/0fsswq296aczj16q6harvnx0999bm1nspi0vcyrppj0qcbngky00.nar.xz

This header will be included in the HTTP response when the client fetches this object from the cache. All the Fastly VCL has to do is look for the existence of this header, and then push the URL contained within it (see [h2.push()](<https://docs.fastly.com/vcl/functions/h2-push/>) for more information) to the client. This is the "only" thing the VCL has to really do.

<aside> 💡 Insight: Fastly VCL currently can't do body processing, that is, looking at or manipulating the content body of an HTTP response. We can, however, look at the HTTP headers instead, which can be done by uploading metadata through S3, which it will reply with during the HTTP response.

</aside>

<aside> 👉 Heads up: Because this part must be implemented in the nix copy step, and we can only push objects that have the right S3 metadata set in their response headers, only newer objects in the cache can support server push. Old channels and uploads will not push objects.

</aside>