From "Smart Pre-Fetching: Varnish @ Yakaz.com by Pierre-Gilles Mialon, Yakaz.com"
Use varnish as a memcached with HTTP interface. Push data updates directly into the cache without having to buffer them outside varnish and pulling them through a backend.
- no backend! the content is echoed by another varnish thread through vcl_error()
- the content lives only in the varnish cache until it expires or the cache is cleared
- gzip compression is handled by varnish. add "FC-Content-Encoding: gzip" to the request for client-side compression
PoC Limits:
- default usable data size: ~5kB before base64-encoding, after optional client-side gzip
- default total request header limit: 8kB (run-time parameter http_req_hdr_len). exceeding the limit results in "413 Request Entity Too Large"
- default total request size limit: 32kB (run-time parameter http_req_size). exceeding the limit results in a connection reset: "11 SessionClose c blast"
- default session workspace limit: 64kB (run-time parameter sess_workspace). should be several times larger than the base64 data
- binary data requires either digest.synthetic_base64_decode (a frankenfunction built from base64_decode and null.synth) or libvmod-null and a FC-Data-Length header
- the request muss not be "pass"ed in VCL or varnish won't cache the response
- cache misses without Forcecontent header will be sent to the default backend unless handled elsewhere in your VCL
- request body is not passed to the backend through a "miss", therefore special request headers must transport the base64-encoded content
- request header size is always limited. future VCL body access could make it work with even larger objects regardless of http_req_(size|hdr_len).
import std;
import digest;
## point to local varnish instance
backend localvarnish {
.host = "127.0.0.1";
.port = "80";
}
sub vcl_recv {
if (req.http.Forcecontent) {
## check signature. use hmac_sha256 instead of hash_sha256 if possible
## TODO: make FC-Content-Type and FC-Cache-Control optional, include in signature if present
if (req.http.FC-Auth && req.http.FC-TS && req.http.FC-Content-Type &&
(now - std.duration(req.http.FC-TS + "s", 0s)) < 300s &&
req.http.FC-Auth == digest.hash_sha256(req.http.FC-TS + req.http.Forcecontent + "s3cretf00" + req.http.FC-Content-Type)) {
if (req.http.FC-Echo == "1") {
## echo content
error 623;
} else {
## invoke varnish parrot
set req.http.FC-Echo = "1";
set req.hash_ignore_busy = true;
set req.hash_always_miss = true;
set req.backend = localvarnish;
return(lookup);
}
} else {
error 403 "Unauthorized";
}
}
}
sub vcl_fetch {
if (req.http.Forcecontent) {
## gzip the response before caching it
## TODO: maybe blacklist (image|video)/ instead and/or
## compress only small objects?
if (beresp.http.Content-Type ~ "^(text|application)/") {
set beresp.do_gzip = true;
}
}
}
sub vcl_error {
## the varnish parrot
## echo content passed in Forcecontent
if (obj.status == 623) {
set obj.status = 200;
set obj.response = "Ok";
## set response Content-Type
if (req.http.FC-Content-Type) {
set obj.http.Content-Type = req.http.FC-Content-Type;
} else {
set obj.http.Content-Type = "application/octet-stream";
}
## set response Content-Encoding
if (req.http.FC-Content-Encoding) {
set obj.http.Content-Encoding = req.http.FC-Content-Encoding;
}
## set response Cache-Control
if (req.http.FC-Cache-Control) {
set obj.http.Cache-Control = req.http.FC-Cache-Control;
} else {
## v-maxage needs VCL support to set beresp.ttl
set obj.http.Cache-Control = "v-maxage=4294967295, max-age=300";
}
if (req.http.FC-Base64) {
## synthetic() expects a null-terminated string!
synthetic("" + digest.base64_decode(req.http.Forcecontent));
## for binary data, use synthetic_base64_decode:
#digest.synthetic_base64_decode("" + req.http.Forcecontent);
## or use libvmod-null and pass the decoded length in a request header
#null.synth(digest.base64_decode(req.http.Forcecontent), req.http.FC-Data-Length);
} else {
synthetic("" + req.http.Forcecontent);
}
return(deliver);
}
}
#!/bin/bash
#set -x
key="$1"
valuetype="$2"
value="`cat|base64 -w0`"
ts="`date "+%s"`"
curl -H "FC-Auth: `echo -n "${ts}${value}s3cretf00${valuetype}"|sha256sum|awk '{ print $1 }'`" \
-H "FC-TS: ${ts}" \
-H "FC-Base64: 1" \
-H "FC-Content-Type: ${valuetype}" \
-H "Forcecontent: ${value}" \
-I ${key}
## text works well
echo "Updated at `date`" | ./fc.sh http://parrot.example.com/foo text/plain
## even binary content
convert bigpicture.jpg -resize 32x32 jpg:- | ./fc.sh http://parrot.example.com/bar image/jpeg
curl -i http://parrot.example.com/foo
Hi,
I have made some modification in my fork, now when an object is updated in the cache, we ensure that the old version will be remove in the next 4 minutes from the cache storage.