I'd like to make a case for some additional control of Vary processing by VCL.

As everyone knows, the HTTP standard Vary header is pretty limited in terms of 
flexibility. It only supports specifying header names, and when used in a naive 
way, i.e. for headers which clients fill with greatly varying content or 
format, it can be disastrous to your cache efficiency. Good examples of this 
are the Cookie, Accept-Language, User-Agent headers.

Fortunately Varnish provides a powerful way to influence this with VCL, and 
headers can be filtered, normalized or cleaned up before they hit the cache 
lookup stage. This works well for headers with well known, predefined keys or 
values: specific strings can be tested for in VCL, and be removed from the 
header if not relevant for the cache lookup. With a bit more string (list) 
processing, this could even be easier and more powerful with list filtering, 
sorting capability for canonical ordering etc., but that's the sort of thing 
that could be implemented in a VMOD.

What isn't currently very easy and straightforward to do in Varnish, is using 
information sent by the backend for Vary processing. Sometimes it's not easy or 
even nearly impossible to put the logic for preprocessing/sanitizing the varied 
headers in VCL; for example because it varies (no pun intended) a lot across 
all served content. In that case it could be nice to have the backend provide 
support for the Vary preprocessing logic in VCL, for example by sending a list 
of acceptable values for a header that is varied on for that particular URL, in 
a response header.

Take for example the Accept-Language header. Although fortunately not required 
for the majority of content we serve, we do have a need to Vary on that header 
for some wikis in the Wikimedia cluster, for language wikis with multiple 
variants. Obviously, simply adding the header to the Vary list without anything 
else would simply destroy the caching. Users across the world send wildly 
different Accept-Language based on their location and browser settings. Doing 
some regex filtering and sanitization of the header would help a bit, but it's 
still far from optimal.

What we've done in the last 5 years with Squid is to have our backend 
(MediaWiki) send a header specifying what header content should be varied on, 
and what should be ignored. It's handled by a patch to Squid, and it's 
described here:

        http://www.squid-cache.org/mail-archive/squid-dev/200802/0085.html

This has worked very well for us and has resulted in a very good cache 
efficiency, while still providing some flexibility at the cache level that is 
being directed by the backend. This was convenient, as the backend is in 
control of the cache variance when and where it's needed. It knows its content 
and its needs best, and Squid doesn't allow for much logic and flexibility in 
its configuration anyway.

With our migration to Varnish, we can do a large part of the necessary cache 
varying processing in VCL, as described above. Accept-Encoding (compression) 
normalization is already better handled by Varnish internally, most cookie 
names/values are pretty consistent across our content set (in _our_ 
application, but YMMV!) and with a few hacks in VCL we can mostly get there. 
But the Accept-Language issue as described above is a tougher one, as the 
acceptable values depend on which wiki/language is being served, and which 
variants are available for that particular URL. We've looked at porting (a 
variant of) the X-Vary-Options header to Varnish, using it in some way with VCL 
for the logic behind it, but it seems messy and suboptimal without patching 
Varnish itself, which isn't a very nice solution either.

It seems that at the root of the problem is the current lack of ability in VCL 
to influence the Vary processing, while having an object from the cache 
available for lookup of a response header. Something like a VCL hook, between 
the result of a cache lookup hit, but before additional Vary processing/lookup 
is done. Conceptually, assuming the Vary response headers by the backend for 
Vary direction (like Vary and X-Vary-Options) are consistent across all 
variants, a random (or first) variant object at the hash key location could be 
used for "obj" in VCL in the hook. The VCL hook can learn about which headers 
are varied on (Vary resp. header), possibly get the allowed values for each 
header (e.g. a list of available languages for this url, "Available-Languages: 
sr-ec,sr-el"), and can filter the Accept-Language header in preparation of the 
Vary processing by Varnish. Even nicer if it could influence the Vary string 
generation itself, similar to how hash_data() works.

Right now a hook between the cache lookup hit and the Vary processing step 
doesn't exist, which makes this clunky. You can probably sort of do it with 
restart today:

1) In vcl_recv() or vcl_lookup(), guess which headers might be in the Vary 
header, and rename those out of the way
2) in vcl_hit(), check the Vary response header for which headers are varying, 
sanitize those from the saved values, restore them under original name, restart 
the request
3) Make sure to skip 1) and 2) in the restarted request. vcl_recv, vcl_lookup, 
and the cache lookup are repeated although this isn't really necessary.

This probably works, but VCL has limited info in step 1), and the necessary 
request restart makes things more complex and requires the rest of the VCL code 
to handle that, while it's not really obvious.

I think a VCL hook between the lookup hit and vary processing stage would go a 
long way toward solving this problem in a clean way. This should allow for a 
lot of flexibility for optimizing cache variance, while not sacrificing 
performance. It could easily be used for things like the Cookie header as well, 
when the "allowed" cookies are not easily hardcoded in VCL. One possible 
catch/frequent mistake I see to such a VCL hook: it does require people to be 
aware that the "obj" reference they get in that VCL hook would not be the final 
variant object that will eventually deliver the object...

Additionally it could be nice to be able to restart just a lookup in 
vcl_hit/vcl_miss, to be able to modify the request "to make a cache hit work" 
without restarting everything, for some other use cases as well. But I can also 
see problems with introducing such a loop. Right now it looks like VCL has no 
cycles besides the big "restart" of the request, and this would change that.

As far as I can see from phk's flow diagram of last month, this also doesn't 
seem possible with the Varnish 4.0 vcl_lookup() plan[1] either. Considering the 
future of the VCL state machine flow and the VCL hooks is now under discussion, 
I thought I'd throw this in there. :)

[1] 
https://www.varnish-cache.org/lists/pipermail/varnish-dev/2013-April/007529.html

-- 
Mark Bergsma <[email protected]>
Lead Operations Architect
Wikimedia Foundation





_______________________________________________
varnish-dev mailing list
[email protected]
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev

Reply via email to