Skip to content

Commit 85ec945

Browse files
committed
Add test for dead-end backends
The code path for launching a dead-end backend because we're out of slots was not covered by any tests, so add one. (Some tests did hit the case of launching a dead-end backend because the server is still starting up, though, so the gap in our test coverage wasn't as big as it sounds.) Reviewed-by: Andres Freund <[email protected]> Discussion: https://www.postgresql.org/message-id/[email protected]
1 parent 6a1d0d4 commit 85ec945

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

src/test/perl/PostgreSQL/Test/Cluster.pm

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ use File::Path qw(rmtree mkpath);
104104
use File::Spec;
105105
use File::stat qw(stat);
106106
use File::Temp ();
107+
use IO::Socket::INET;
107108
use IPC::Run;
108109
use PostgreSQL::Version;
109110
use PostgreSQL::Test::RecursiveCopy;
@@ -291,6 +292,83 @@ sub connstr
291292

292293
=pod
293294
295+
=item $node->raw_connect()
296+
297+
Open a raw TCP or Unix domain socket connection to the server. This is
298+
used by low-level protocol and connection limit tests.
299+
300+
=cut
301+
302+
sub raw_connect
303+
{
304+
my ($self) = @_;
305+
my $pgport = $self->port;
306+
my $pghost = $self->host;
307+
308+
my $socket;
309+
if ($PostgreSQL::Test::Utils::use_unix_sockets)
310+
{
311+
require IO::Socket::UNIX;
312+
my $path = "$pghost/.s.PGSQL.$pgport";
313+
314+
$socket = IO::Socket::UNIX->new(
315+
Type => SOCK_STREAM(),
316+
Peer => $path,
317+
) or die "Cannot create socket - $IO::Socket::errstr\n";
318+
}
319+
else
320+
{
321+
$socket = IO::Socket::INET->new(
322+
PeerHost => $pghost,
323+
PeerPort => $pgport,
324+
Proto => 'tcp'
325+
) or die "Cannot create socket - $IO::Socket::errstr\n";
326+
}
327+
return $socket;
328+
}
329+
330+
=pod
331+
332+
=item $node->raw_connect_works()
333+
334+
Check if raw_connect() function works on this platform. This should
335+
be called to SKIP any tests that require raw_connect().
336+
337+
This tries to connect to the server, to test whether it works or not,,
338+
so the server is up and running. Otherwise this can return 0 even if
339+
there's nothing wrong with raw_connect() itself.
340+
341+
Notably, raw_connect() does not work on Unix domain sockets on
342+
Strawberry perl 5.26.3.1 on Windows, which we use in Cirrus CI images
343+
as of this writing. It dies with "not implemented on this
344+
architecture".
345+
346+
=cut
347+
348+
sub raw_connect_works
349+
{
350+
my ($self) = @_;
351+
352+
# If we're using Unix domain sockets, we need a working
353+
# IO::Socket::UNIX implementation.
354+
if ($PostgreSQL::Test::Utils::use_unix_sockets)
355+
{
356+
diag "checking if IO::Socket::UNIX works";
357+
eval {
358+
my $sock = $self->raw_connect();
359+
$sock->close();
360+
};
361+
if ($@ =~ /not implemented/)
362+
{
363+
diag "IO::Socket::UNIX does not work: $@";
364+
return 0;
365+
}
366+
}
367+
return 1
368+
}
369+
370+
=pod
371+
294372
=item $node->group_access()
295373
296374
Does the data dir allow group access?

src/test/postmaster/t/001_connection_limits.pl

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ sub background_psql_as_user
4343
}
4444

4545
my @sessions = ();
46+
my @raw_connections = ();
4647

4748
push(@sessions, background_psql_as_user('regress_regular'));
4849
push(@sessions, background_psql_as_user('regress_regular'));
@@ -69,11 +70,50 @@ sub background_psql_as_user
6970
"superuser_reserved_connections limit",
7071
expected_stderr => qr/FATAL: sorry, too many clients already/);
7172

72-
# TODO: test that query cancellation is still possible
73+
# We can still open TCP (or Unix domain socket) connections, but
74+
# beyond a certain number (roughly 2x max_connections), they will be
75+
# "dead-end backends".
76+
SKIP:
77+
{
78+
skip "this test requies working raw_connect()" unless $node->raw_connect_works();
79+
80+
for (my $i = 0; $i <= 20; $i++)
81+
{
82+
my $sock = $node->raw_connect();
83+
84+
# On a busy system, the server might reject connections if
85+
# postmaster cannot accept() them fast enough. The exact limit
86+
# and behavior depends on the platform. To make this reliable,
87+
# we attempt SSL negotiation on each connection before opening
88+
# next one. The server will reject the SSL negotations, but
89+
# when it does so, we know that the backend has been launched
90+
# and we should be able to open another connection.
91+
92+
# SSLRequest packet consists of packet length followed by
93+
# NEGOTIATE_SSL_CODE.
94+
my $negotiate_ssl_code = pack("Nnn", 8, 1234, 5679);
95+
my $sent = $sock->send($negotiate_ssl_code);
96+
97+
# Read reply. We expect the server to reject it with 'N'
98+
my $reply = "";
99+
$sock->recv($reply, 1);
100+
is($reply, "N", "dead-end connection $i");
73101

102+
push(@raw_connections, $sock);
103+
}
104+
}
105+
106+
# TODO: test that query cancellation is still possible. A dead-end
107+
# backend can process a query cancellation packet.
108+
109+
# Clean up
74110
foreach my $session (@sessions)
75111
{
76112
$session->quit;
77113
}
114+
foreach my $socket (@raw_connections)
115+
{
116+
$socket->close();
117+
}
78118

79119
done_testing();

0 commit comments

Comments
 (0)