|
| 1 | + |
| 2 | +# Copyright (c) 2021-2024, PostgreSQL Global Development Group |
| 3 | + |
| 4 | +# Test postmaster start and stop state machine. |
| 5 | + |
| 6 | +use strict; |
| 7 | +use warnings FATAL => 'all'; |
| 8 | +use PostgreSQL::Test::Cluster; |
| 9 | +use PostgreSQL::Test::Utils; |
| 10 | +use Test::More; |
| 11 | + |
| 12 | +# |
| 13 | +# Test that dead-end backends don't prevent the server from shutting |
| 14 | +# down. |
| 15 | +# |
| 16 | +# Dead-end backends can linger until they reach |
| 17 | +# authentication_timeout. We use a long authentication_timeout and a |
| 18 | +# much shorter timeout for the "pg_ctl stop" operation, to test that |
| 19 | +# if dead-end backends are killed at fast shut down. If they're not, |
| 20 | +# "pg_ctl stop" will error out before the authentication timeout kicks |
| 21 | +# in and cleans up the dead-end backends. |
| 22 | +my $authentication_timeout = $PostgreSQL::Test::Utils::timeout_default; |
| 23 | +my $stop_timeout = $authentication_timeout / 2; |
| 24 | + |
| 25 | +# Initialize the server with low connection limits, to test dead-end backends |
| 26 | +my $node = PostgreSQL::Test::Cluster->new('main'); |
| 27 | +$node->init; |
| 28 | +$node->append_conf('postgresql.conf', "max_connections = 5"); |
| 29 | +$node->append_conf('postgresql.conf', "max_wal_senders = 0"); |
| 30 | +$node->append_conf('postgresql.conf', "autovacuum_max_workers = 1"); |
| 31 | +$node->append_conf('postgresql.conf', "max_worker_processes = 1"); |
| 32 | +$node->append_conf('postgresql.conf', "log_connections = on"); |
| 33 | +$node->append_conf('postgresql.conf', "log_min_messages = debug2"); |
| 34 | +$node->append_conf('postgresql.conf', |
| 35 | + "authentication_timeout = '$authentication_timeout s'"); |
| 36 | +$node->append_conf('postgresql.conf', 'trace_connection_negotiation=on'); |
| 37 | +$node->start; |
| 38 | + |
| 39 | +if (!$node->raw_connect_works()) |
| 40 | +{ |
| 41 | + plan skip_all => "this test requires working raw_connect()"; |
| 42 | +} |
| 43 | + |
| 44 | +my @raw_connections = (); |
| 45 | + |
| 46 | +# Open a lot of TCP (or Unix domain socket) connections to use up all |
| 47 | +# the connection slots. Beyond a certain number (roughly 2x |
| 48 | +# max_connections), they will be "dead-end backends". |
| 49 | +for (my $i = 0; $i <= 20; $i++) |
| 50 | +{ |
| 51 | + my $sock = $node->raw_connect(); |
| 52 | + |
| 53 | + # On a busy system, the server might reject connections if |
| 54 | + # postmaster cannot accept() them fast enough. The exact limit |
| 55 | + # and behavior depends on the platform. To make this reliable, |
| 56 | + # we attempt SSL negotiation on each connection before opening |
| 57 | + # next one. The server will reject the SSL negotations, but |
| 58 | + # when it does so, we know that the backend has been launched |
| 59 | + # and we should be able to open another connection. |
| 60 | + |
| 61 | + # SSLRequest packet consists of packet length followed by |
| 62 | + # NEGOTIATE_SSL_CODE. |
| 63 | + my $negotiate_ssl_code = pack("Nnn", 8, 1234, 5679); |
| 64 | + my $sent = $sock->send($negotiate_ssl_code); |
| 65 | + |
| 66 | + # Read reply. We expect the server to reject it with 'N' |
| 67 | + my $reply = ""; |
| 68 | + $sock->recv($reply, 1); |
| 69 | + is($reply, "N", "dead-end connection $i"); |
| 70 | + |
| 71 | + push(@raw_connections, $sock); |
| 72 | +} |
| 73 | + |
| 74 | +# When all the connection slots are in use, new connections will fail |
| 75 | +# before even looking up the user. Hence you now get "sorry, too many |
| 76 | +# clients already" instead of "role does not exist" error. Test that |
| 77 | +# to ensure that we have used up all the slots. |
| 78 | +$node->connect_fails("dbname=postgres user=invalid_user", |
| 79 | + "connect ", |
| 80 | + expected_stderr => qr/FATAL: sorry, too many clients already/); |
| 81 | + |
| 82 | +# Open one more connection, to really ensure that we have at least one |
| 83 | +# dead-end backend. |
| 84 | +my $sock = $node->raw_connect(); |
| 85 | + |
| 86 | +# Test that the dead-end backends don't prevent the server from stopping. |
| 87 | +$node->stop('fast', timeout => $stop_timeout); |
| 88 | + |
| 89 | +$node->start(); |
| 90 | +$node->connect_ok("dbname=postgres", "works after restart"); |
| 91 | + |
| 92 | +# Clean up |
| 93 | +foreach my $socket (@raw_connections) |
| 94 | +{ |
| 95 | + $socket->close(); |
| 96 | +} |
| 97 | + |
| 98 | +done_testing(); |
0 commit comments