Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit fab84c7

Browse files
committed
Improve PostgresNode.pm's logic for detecting already-in-use ports.
Buildfarm members bowerbird and jacana have shown intermittent "could not bind IPv4 socket" failures in the BinInstallCheck stage since mid-December, shortly after commits 1caef31 and 9821492 changed the logic for selecting which port to use in temporary installations. One plausible explanation is that we are randomly selecting ports that are already in use for some non-Postgres purpose. Although the code tried to defend against already-in-use ports, it used pg_isready to probe the port which is quite unhelpful: if some non-Postgres server responds at the given address, pg_isready will generally say "no response", leading to exactly the wrong conclusion about whether the port is free. Instead, let's use a simple TCP connect() call to see if anything answers without making assumptions about what it is. Note that this means there's no direct check for a conflicting Unix socket, but that should be okay because there should be no other Unix sockets in use in the temporary socket directory created for a test run. This is only a partial solution for the TCP case, since if the port number is in use for an outgoing connection rather than a listening socket, we'll fail to detect that. We could try to bind() to the proposed port as a means of detecting that case, but that would introduce its own failure modes, since the system might consider the address to remain reserved for some period of time after we drop the bound socket. Close study of the errors returned by bowerbird and jacana suggests that what we're seeing there may be conflicts with listening not outgoing sockets, so let's try this and see if it improves matters. It's certainly better than what's there now, in any case. Michael Paquier, adjusted by me to work on non-Windows as well as Windows
1 parent 8f91d87 commit fab84c7

File tree

1 file changed

+27
-13
lines changed

1 file changed

+27
-13
lines changed

src/test/perl/PostgresNode.pm

+27-13
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ use File::Spec;
9090
use File::Temp ();
9191
use IPC::Run;
9292
use RecursiveCopy;
93+
use Socket;
9394
use Test::More;
9495
use TestLib ();
9596
use Scalar::Util qw(blessed);
@@ -98,14 +99,15 @@ our @EXPORT = qw(
9899
get_new_node
99100
);
100101

101-
our ($test_pghost, $last_port_assigned, @all_nodes);
102+
our ($test_localhost, $test_pghost, $last_port_assigned, @all_nodes);
102103

103104
INIT
104105
{
105106
# PGHOST is set once and for all through a single series of tests when
106107
# this module is loaded.
108+
$test_localhost = "127.0.0.1";
107109
$test_pghost =
108-
$TestLib::windows_os ? "127.0.0.1" : TestLib::tempdir_short;
110+
$TestLib::windows_os ? $test_localhost : TestLib::tempdir_short;
109111
$ENV{PGHOST} = $test_pghost;
110112
$ENV{PGDATABASE} = 'postgres';
111113

@@ -347,7 +349,7 @@ sub set_replication_conf
347349
else
348350
{
349351
print $hba
350-
"host replication all 127.0.0.1/32 sspi include_realm=1 map=regress\n";
352+
"host replication all $test_localhost/32 sspi include_realm=1 map=regress\n";
351353
}
352354
close $hba;
353355
}
@@ -839,19 +841,31 @@ sub get_new_node
839841

840842
while ($found == 0)
841843
{
842-
# wrap correctly around range end
844+
# advance $port, wrapping correctly around range end
843845
$port = 49152 if ++$port >= 65536;
844-
print "# Checking for port $port\n";
845-
if (!TestLib::run_log([ 'pg_isready', '-p', $port ]))
846+
print "# Checking port $port\n";
847+
848+
# Check first that candidate port number is not included in
849+
# the list of already-registered nodes.
850+
$found = 1;
851+
foreach my $node (@all_nodes)
846852
{
847-
$found = 1;
853+
$found = 0 if ($node->port == $port);
854+
}
848855

849-
# Found a potential candidate port number. Check first that it is
850-
# not included in the list of registered nodes.
851-
foreach my $node (@all_nodes)
852-
{
853-
$found = 0 if ($node->port == $port);
854-
}
856+
# Check to see if anything else is listening on this TCP port.
857+
# This is *necessary* on Windows, and seems like a good idea
858+
# on Unixen as well, even though we don't ask the postmaster
859+
# to open a TCP port on Unix.
860+
if ($found == 1)
861+
{
862+
my $iaddr = inet_aton($test_localhost);
863+
my $paddr = sockaddr_in($port, $iaddr);
864+
my $proto = getprotobyname("tcp");
865+
866+
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die;
867+
$found = 0 if connect(SOCK, $paddr);
868+
close(SOCK);
855869
}
856870
}
857871

0 commit comments

Comments
 (0)