Back to blog

Follow and Subscribe

Reusing backend connections to increase performance

Rogier Mulhuijzen

Senior Professional Services Engineer

Reusing connections between your Varnish instance and your backends (origins) is a good idea for multiple reasons. If your Varnish is on the same network as your backends and you're doing low volume traffic, you can stop reading, because a) the difference will probably be negligible, and b) you're probably already reusing backend connections.

To find out whether or not connections are being reused, use the following command

me@example:~$ varnishstat -1 -f backend_conn,backend_reuse
backend_conn          3881846        4.22 Backend conn. success
backend_reuse       191288113      208.34 Backend conn. reuses

The first column is the stat name, where backend_conn is the number of times a connection has been made, and backend_reuse is how many times it has been reused. The second column is the value since your Varnish was started, and the third column is the average per second over that period.

What you're looking for is for backend_reuse to not be zero, and ideally to be an order of magnitude, or more, higher than backend_conn. The values in the example above are pretty ideal. It makes the occasional connection, but the vast majority of requests are made through already existing connections.

You can also run the varnishstat command without the -1 and it will give you an additional column, which will show the change in the values for the past second.

Before I dive into how you can improve these numbers if they're bad, let's go over some of the reasons why reusing is a good idea.

Why reuse connections?

As I mentioned above, there are two conditions where reusing is important. The most crucial is if there's a high round-trip time (RTT) between your Varnish and the backends. The other is running a high-traffic site, with thousands of requests per second. If you have a RTT that's below a millisecond, you'll find that the improvements of reusing connections are pretty small, but due to the high volume, small improvements can have a big impact.

I will start, however, with the most important reason: you can run out of port numbers. Every connection is uniquely identified by a tuple consisting of the source and destination IPs as well as the source and destination ports. Out of those four, only the source port is not static between your Varnish and a single backend. There are usually thousands of ports available (and with some tweaking of your operating system, about 64K). After a connection is closed, your operating system will keep the tuple around for anywhere between 30 seconds and 2 minutes, and refuse to use that exact tuple for any new connections to avoid delayed data from throwing a spanner in the works. These connections are in the so-called TIME_WAIT status. Most operating systems allow you to tune how long connections stay in TIME_WAIT after they're closed, but it's not recommended to lower that value, and you can avoid the problem by reusing connections.

A bit of back-of-the-paper-napkin math shows that if you have tuned your OS to use all port numbers above 1024 for outgoing connections, and your OS keeps closed connections in the TIME_WAIT status for 60 seconds, you can do roughly 1066 connections per second to a single backend before running out of ports.

The second reason to reuse connections is that making the connection has a cost. On the network side, it takes a round trip before a request can be sent. But even if that's not a problem because your RTT is low, it still takes some effort from your operating system. Other than some buffers, it also has to allocate a file descriptor, which usually takes a linear search to find the lowest available number, and can't be parallelized. In benchmarks I've seen, reusing connections make a 300-400% difference in throughput, and that was with Varnish and the backends on the same local network.

The third reason is TCP window scaling. While a TCP connection is in use, both ends adjust the congestion window (amount of data allowed to be on the wire at once) to allow for greater transfer speeds if possible. Of course, the higher the RTT, the more impact this has, but even on a local network it still matters for multi-megabyte files. When reusing connections, previous requests will usually have caused the window to have reached its sweet spot already and responses will be transferred as fast as your network allows.

How to make sure connections are reused

There are key several elements to check if you don't see connections being reused enough.

The protocol version

First of all, make sure that your backends are capable of doing HTTP/1.1. It seems pretty amazing, but 16 years after HTTP/1.1 was introduced, there are still HTTP/1.0 servers in the wild.

To check whether or not your backends are replying with HTTP/1.1 or HTTP/1.0, use the following command:

me@example:~$ varnishtop -b -i RxProtocol

The -b filters the Varnish log down to just backend communications. The -i RxProtocol narrows it down further to just the protocol part of the response.

You should see something like:

list length 2                                    example

  6612.25 RxProtocol        HTTP/1.1
     1.82 RxProtocol        HTTP/1.0

The numbers reflect how many times in the last minute a certain line was seen in the log. Obviously we would like to see nothing but HTTP/1.1, but the occasional spurious HTTP/1.0 can still occur. If the ratio is anything like the example above, then you have nothing to worry about. You're not necessarily looking for perfection here.

However, if you see a larger amount of HTTP/1.0 requests than you'd like, you can use the following command to see all the backend transactions that have a HTTP/1.0 response:

me@example:~$ varnishlog -b -m RxProtocol:HTTP/1.0

One of the things to look at is BackendOpen. That gives you the name of the backend in your VCL, as well as the IPs and ports of the connection. You'll also see all the request and response headers.

How to go about making your backend respond using HTTP/1.1 completely depends on your backend. Check its documentation or contact the vendor. Upgrading to a newer version of the software in question can also help.

Persistent connections

HTTP/1.1 considers all connections persistent (able to be reused) unless explicitly marked otherwise. The header used to mark a connection as not persistent is Connection: close. To see how many Connection: close headers you get per second, you can use:

me@example:~$ varnishtop -bC -i RxHeader -I connection.*close

To look at those transactions in detail:

me@example:~$ varnishlog -bC -m RxHeader:connection.*close

Again, how to get your backend to stop sending the Connection: close headers and actually reuse the connections is something that is very specific. Look in the documentation, contact the vendor, and consider upgrading.

Maximum number of requests

Some web servers will only do a certain number of requests per connection. When the maximum is reached, they will send a Connection: close header and close the connection after the response is completed.

Apache, for instance, has the setting MaxKeepAliveRequests which defaults to 100. Setting this to 0 will allow Apache to handle an infinite number of requests on a connection.

Keep-alive timeouts

To conserve resources, web servers close connections after they've been idle for a while. This is often referred to as the keep-alive timeout. By default, Varnish will close connections from clients after 10 seconds of no activity and Apache after 5 seconds. In contrast, Varnish will keep connections to backends open for as long as it can. If random clients (i.e. users on the internet) are not able to connect directly to your backend, there really is no good reason to keep the keep-alive timeout short. Think minutes instead of seconds.

In Apache the setting is KeepAliveTimeout, and most other software will have something close to that.

Network device timeouts

Something to keep in mind is that there might be stateful firewalls or NAT devices between your Varnish and your backends. These tend to have timeouts on TCP connections, too. If those are lower than the keep-alive timeout on your backends, the network devices will not know what to do with the traffic on the connection Varnish is trying to reuse. They will either drop it and cause timeouts, or send resets and cause Varnish to retry the request if it can.

Check the whole network path between your Varnish and your backends, and make sure that the timeouts for open connections are higher than the keep-alive timeout on your backends.

TL;DR

Reusing connections highly benefits performance between your Varnish and your backends. Make sure you're not using HTTP/1.0, that your backends are not sending Connection: close headers, that you raise timeouts, and that you check network timeouts on all devices in the path.