Ignore:
Timestamp:
2011-12-01T12:22:04+13:00 (12 years ago)
Author:
jmt12
Message:

Added in fix for 'socket pool exhausted' problem (see code for details) and tightened up shutdown/close code to try and lessen the exhaustion problem

File:
1 edited

Legend:

Unmodified
Added
Removed
  • gs2-extensions/parallel-building/trunk/src/perllib/SocketsSwimmingThreadPoolClient.pm

    r24685 r24838  
    99use strict;
    1010use warnings;
     11
     12my $retry_time = 30;
    1113
    1214sub new
     
    2325}
    2426
     27sub open_socket
     28{
     29  my $self = shift @_;
     30  # There is, unfortunately, a complication when using TCP sockets - that being
     31  # the amount of time sockets linger after being closed. This TIME_WAIT period
     32  # is required to ensure the terminating ACKs (kernel level) are recieved (in
     33  # a graceful disconnect) and to allow time for 'wandering duplicates' to
     34  # finally arrive. While there are (platform/OS/hardware) methods for getting
     35  # rid of / shortening this grace period they aren't recommended (for both
     36  # stability and security reasons).
     37  # Thus you have the issue that, very occasionally and somewhat based upon the
     38  # current turnover rate of TCP connections, you may exhaust the available
     39  # pool of TCP sockets (as the rest are stuck waiting on TIME_WAIT timeouts).
     40  # While the 'reuse' flag might lead you to expect the ability to 'reuse' the
     41  # socket - there is one glaring caveat: you can't reuse the port from the
     42  # same origin address as this would be a major security flaw. So in reality
     43  # the reuse flag does nothing for us (which is why I removed it).
     44  # Instead I am forced to put the socket connection in a loop and, if the
     45  # first attempt to create the socket fails, wait around for SO_LINGER time
     46  # in the hope that the TCP socket pool will have finally purged a number of
     47  # the stranded TIME_WAIT connections.
     48  # One last kick in the daddy-bags - the *default* SO_LINGER is set to 2*MSL
     49  # (Maximum Segment Lifetime - the 'Time To Live of TCP' packets). This means
     50  # (according to RFC793) we may be waiting up to 4 minutes for TIME_WAITs to
     51  # be reaped. Sigh. Still - ever an optimist - I'll retry the socket
     52  # connection every 30 seconds.
     53  # References:
     54  #   http://www.perlmonks.org/?node_id=771242
     55  #   http://hea-www.harvard.edu/~fine/Tech/addrinuse.html
     56  #   http://blog.port80software.com/2004/12/07/hurry-up-and-time_wait/
     57  #   http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/
     58  #   http://www.faqs.org/rfcs/rfc793.html
     59  while(1)
     60  {
     61    my $socket= new IO::Socket::INET(PeerAddr => $self->{host} || 'localhost',
     62                                     PeerPort => $self->{port} || 8190,
     63                                     Proto => 'tcp');
     64    if ($socket)
     65    {
     66      return $socket;
     67    }
     68    print "Failed to create client socket: $!\n";
     69    print "=> Most likely cause - TCP ephemeral ports exhausted (stuck in TIME_WAITs)\n";
     70    print "=> Retry in " . $retry_time . " seconds.\n";
     71  }
     72}
     73
    2574sub query
    2675{
    2776  my ($self, $query)= @_;
    28   my $socket= new IO::Socket::INET(PeerAddr => $self->{host} || 'localhost',
    29                                    PeerPort => $self->{port} || 8191,
    30                                    Proto => 'tcp');
    31   croak "$!. Is the server running?\n" unless $socket;
     77  my $socket = $self->open_socket();
     78  croak "Fatal Error! $!\nDetails: " . $self->{'host'} . ":" . $self->{'port'} . "\n" unless $socket;
    3279  binmode($socket, ":utf8");
    3380  print $socket $query . "\n.\n";
     
    3986    last if $reply =~ s/\n\.\n$//;
    4087  }
    41   $socket->shutdown(2);
     88  close($socket);
    4289  return $reply;
    4390}
Note: See TracChangeset for help on using the changeset viewer.