Woodgnome
Woodgnome

Reputation: 2391

Why is Varnish fetch significantly slower than bypassing Varnish?

I've set up Varnish (port 80) to cache requests from a Node.js server (port 8080) that usually responds in single-digit miliseconds (but some times significantly longer).

I set up a simple benchmark in PHP (I'm aware that'll only send one request at a time):

// Measure time for backend (bypassing Varnish by using port 8080)
$t = microtime(true);
foreach ($requests as $request){
  file_get_contents($request["url"] . ":8080/?" . http_build_query($request["parameters"]));
}
$t = microtime(true) - $t;
echo "Backend: " . $t . "\n";

// Measure time for Varnish (using port 80 after Varnish restart)
$t = microtime(true);
foreach ($requests as $request){
  file_get_contents($request["url"] . ":80/?" . http_build_query($request["parameters"]));
}
$t = microtime(true) - $t;
echo "Varnish MISS: " . $t . "\n";

// Measure time for Varnish (now cached)
$t = microtime(true);
foreach ($requests as $request){
  file_get_contents($request["url"] . ":80/?" . http_build_query($request["parameters"]));
}
$t = microtime(true) - $t;
echo "Varnish HIT: " . $t . "\n";

// Results:
Backend: ~10 seconds
Varnish MISS: ~65 seconds
Varnish HIT: ~2 seconds

I test with ~1400 requests. On average Varnish adds 30-40 miliseconds of overhead to each request that is missed.

The overhead from a cache miss in Varnish seems obscenely large and I've found others reporting similar results - no one able to explain why or how to reduce the overhead, however.

So my question is, why is there such a large overhead and how can I reduce it?

I know that the idea of caching is to avoid optimizing the response time of the initial request, but this particular web service will see a large amount of unique requests in a short time (but many re-requests over a couple of days), so the overhead from Varnish is somewhat important.

Varnish setup

vcl 4.0;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
    unset req.http.Cookie;
}

sub vcl_backend_response {
}

sub vcl_deliver {
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}

Upvotes: 1

Views: 810

Answers (1)

Woodgnome
Woodgnome

Reputation: 2391

After asking the Varnish guys about the issue, they suggested I try and replicate it on the same server but different backend. Trying that with an Apache backend there were no issues.

Examining the responses from Varnish closer I noticed that with the Node.js server there would be a Content-Length header (set by Varnish) on the requests that finished quickly, but not on those that finished slowly.

Adding a Content-Length header to the Node.js server response (which is added by default in Apache) fixed the issue.

Upvotes: 2

Related Questions