Hi,<div><br></div><div>We have an open ticket #1252, that I am in the process of creating a patch to fix. The problem here is with regard to waitinglists and failure mode. When a slow backend times out, the requesting client will get it's 503 page and go away, scheduling the waitinglist in the process. One of these parked sessions will then retry, and the rest goes back on the waitinglist. The failing backend doesn't recover, so the same happens on every backend attempt. As there isn't any client communication going on here, Varnish never notices if the client (very likely after n * first_byte_timeout) has given up and gone away. For a popular page the number of sessions coming into the waitinglist (page's hitrate) is going to be much higher than the number leaving (only one every first_byte_timeout). End result is going out of file descriptors as observed in ticket #1252, or hitting session_max as we have observed in a $customer case.</div>
<div><br></div><div>The solution to this was first thought to be simple. When a session comes off the waitinglist, check to see if the connection has been closed. If it has, drop the session. Only this turned out to be quite hard to do. The normal TCP EOF checking can't really be done due to HTTP pipelining. If we try to read data from the socket, we have to store the data away somewhere for reuse, and if the next request is a HTTP post with a large body, we'll run out of httpconn pipeline buffer space. Doing recv(2) with MSG_PEEK won't do for the same reason, as we'll only check to see if the OS buffer has anything there, and if there's pipelining in play we'll get a false positive.</div>
<div><br></div><div>Doing a poll() on the socket was next attempted, with the POLLHUP tantalizingly seeming the perfect state. Unfortunately POLLHUP will only be signaled when both ends agree on the connection being closed, and in this case it's only the client that has closed the connection (FIN received by the server, but no FIN is sent the other way until the socket is closed on the server side). The poll()'s thus returned only POLLIN and POLLOUT.</div>
<div><br></div><div>The Linux specific POLLRDHUP could be used to detect the FIN from the client, giving reliable results that the client has indeed closed it's end of the duplex connection. But on closer thought and a lot of googling, I can't find anything about HTTP clients not being allowed to half-close the TCP connection after sending the request and still expect to read the reply. On the contrary, exotic clients did turn up that does exactly that. So this also turned out not to be a solution.</div>
<div><br></div><div>What we really want to know in this case, is not if the client will be sending us any more data, but if the client is still going to accept our response when(/if) we are able to deliver it. So it is writing data that we need to test. But the HTTP protocol isn't going to allow us to write something just to test if that'll result in TCP RSTs. So next thing I have tested out is SO_KEEPALIVE option on the socket. This will send periodic messages to the client, that it will have to ACK, which should eventually allow the server TCP stack to learn that there are no clients on the other end. On FreeBSD SO_KEEPALIVE is on by default I believe, but on Linux it isn't. So with SO_KEEPALIVE on, the client would send a RST after the socket left FIN_WAIT2 state (60s), which closed it on the server side too, allowing Varnish to get POLLHUP state and kill off the session!</div>
<div><br></div><div>Reading up on SO_KEEPALIVE, there are some caveats though. By default both Linux and FreeBSD won't start sending keep-alives until the connection has been idle for 7200 seconds (2 hours?!). And then only after 9 unsuccessful probes spaced 75 seconds apart will it kill the connection. So it will by default take 2 hours 11 minutes for this to trigger. For a webserver these defaults seems too large and should be lowered. But for the problem at hand I believe the default values will still benefit, as this problem doesn't happen instantly but builds over time when dealing with unresponsive backends. On Linux it is possible to specify the SO_KEEPALIVE idle timer and connection attempts/interval per connection using custom SO_-options.</div>
<div><br></div><div>So as a final solution to this problem, I suggest always setting SO_KEEPALIVE on our sockets. Then add parameter settings for the keep alive idle timer, the count and the interval, with better defaults for a webserver (or maybe just setting the idle time to sess_timeout / 2?). (Docfix FreeBSD / other platforms that can't set the keep alive values per socket). And finally making Varnish poll() the client socket on return from waitinglist and kill the session on POLLHUP.</div>
<div><br></div><div>Any comments to this plan of action would be most welcome, and also any clever ideas that I might have missed to detect this situation in the first place.</div><div><br></div><div>Regards,</div><div>Martin Blix Grydeland</div>
<div><div><br></div>-- <br><div><table border="0" cellpadding="0" cellspacing="0" style="font-size:12px;line-height:1.5em;font-family:'Helvetica Neue',Arial,sans-serif;color:rgb(102,102,102);width:550px;border-top-width:1px;border-top-style:solid;border-top-color:rgb(238,238,238);border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);margin-top:20px;padding-top:5px;padding-bottom:5px">
<tbody><tr><td width="100"><a href="http://varnish-software.com" target="_blank"><img src="http://www.varnish-software.com/static/media/logo-email.png"></a><span></span><span></span></td><td><strong style="font-size:14px;color:rgb(34,34,34)">Martin Blix Grydeland</strong><br>
Senior Developer | Varnish Software AS<br>Cell: +47 21 98 92 60<br><span style="font-weight:bold">We Make Websites Fly!</span></td></tr></tbody></table></div>
</div>