From 4d660610d36b314e8e3106beb2c93b072ee505a2 Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Fri, 27 Aug 2021 08:18:40 -0400 Subject: [PATCH v33 8/8] Test: Few tap tests for wal prohibited system Does following testing: 1. Basic verification like insert into normal and unlogged table on wal prohibited system. 2. Check permission to non-superuser to alter wal prohibited system state. 3. Verify open write transaction disconnection when system state has been changed to wal prohibited. 4. Verify wal write and checkpoint lsn after restart of wal prohibited system doesn't change along with wal prohibited state. 5. At restart wal prohibited system shutdown and on start recovery end checkpoint is skipped, verify implicit checkpoint perform when system state changes to wal permitted. 6. Standby server cannot be in wal prohibited, standby.signal and/or recovery.signal take out system from wal prohibited state. --- src/test/recovery/t/026_pg_prohibit_wal.pl | 213 +++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 src/test/recovery/t/026_pg_prohibit_wal.pl diff --git a/src/test/recovery/t/026_pg_prohibit_wal.pl b/src/test/recovery/t/026_pg_prohibit_wal.pl new file mode 100644 index 00000000000..4974059aa9a --- /dev/null +++ b/src/test/recovery/t/026_pg_prohibit_wal.pl @@ -0,0 +1,213 @@ + +# Copyright (c) 2021, PostgreSQL Global Development Group + +# Test wal prohibited state. +use strict; +use warnings; +use PostgresNode; +use TestLib; +use Config; +use Test::More tests => 22; + +# Query to read wal_prohibited GUC +my $show_wal_prohibited_query = "SELECT current_setting('wal_prohibited')"; + +# Initialize database node +my $node_primary = PostgresNode->new('primary'); +$node_primary->init(has_archiving => 1, allows_streaming => 1); +$node_primary->start; + +# Create few tables and insert some data +$node_primary->safe_psql('postgres', <safe_psql('postgres', 'SELECT pg_prohibit_wal(true)'); +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), 'on', + 'server is now wal prohibited'); + +# +# In wal prohibited state, further table insert will fail. +# +# Note that even though inter into unlogged and temporary table doesn't generate +# wal but the transaction does that insert operation will acquire transaction id +# which is not allowed on wal prohibited system. Also, that transaction's abort +# or commit state will be wal logged at the end which is prohibited as well. +# +my ($stdout, $stderr, $timed_out); +$node_primary->psql('postgres', 'INSERT INTO tab VALUES(6)', + stdout => \$stdout, stderr => \$stderr); +like($stderr, qr/cannot execute INSERT in a read-only transaction/, + 'server is wal prohibited, table insert is failed'); +$node_primary->psql('postgres', 'INSERT INTO unlogtab VALUES(6)', + stdout => \$stdout, stderr => \$stderr); +like($stderr, qr/cannot execute INSERT in a read-only transaction/, + 'server is wal prohibited, unlogged table insert is failed'); + +# Get current wal write and latest checkpoint lsn +my $write_lsn = $node_primary->lsn('write'); +my $checkpoint_lsn = get_latest_checkpoint_location($node_primary); + +# Restart the server, shutdown and starup checkpoint will be skipped. +$node_primary->restart; + +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), 'on', + 'server is wal prohibited after restart too'); +is($node_primary->lsn('write'), $write_lsn, + "no wal writes on server, last wal write lsn : $write_lsn"); +is(get_latest_checkpoint_location($node_primary), $checkpoint_lsn, + "no new checkpoint, last checkpoint lsn : $checkpoint_lsn"); + +# Change server to WAL permitted +$node_primary->safe_psql('postgres', 'SELECT pg_prohibit_wal(false)'); +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), + 'off', 'server is change to wal permitted'); + +my $new_checkpoint_lsn = get_latest_checkpoint_location($node_primary); +is($new_checkpoint_lsn == $checkpoint_lsn, 1, + "new checkpoint performed, new checkpoint lsn : $new_checkpoint_lsn"); + +my $new_write_lsn = $node_primary->lsn('write'); +is($new_write_lsn == $write_lsn, 1, + "new wal writes on server, new latest wal write lsn : $new_write_lsn"); + +# Insert data +$node_primary->safe_psql('postgres', 'INSERT INTO tab VALUES(6)'); +is($node_primary->safe_psql('postgres', 'SELECT count(i) FROM tab'), '6', + 'table insert passed'); + +# Only the superuser and the user who granted permission able to call +# pg_prohibit_wal to change wal prohibited state. +$node_primary->safe_psql('postgres', 'CREATE USER non_superuser'); +$node_primary->psql('postgres', 'SELECT pg_prohibit_wal(true)', + stdout => \$stdout, stderr => \$stderr, extra_params => [ '-U', 'non_superuser' ]); +like($stderr, qr/permission denied for function pg_prohibit_wal/, + 'permission denied to non-superuser for alter wal prohibited state'); +$node_primary->safe_psql('postgres', 'GRANT EXECUTE ON FUNCTION pg_prohibit_wal TO non_superuser'); +$node_primary->psql('postgres', 'SELECT pg_prohibit_wal(true)', + stdout => \$stdout, stderr => \$stderr, extra_params => [ '-U', 'non_superuser' ]); +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), 'on', + 'granted permission to non-superuser, able to alter wal prohibited state'); + +# back to normal state +$node_primary->psql('postgres', 'SELECT pg_prohibit_wal(false)'); + +my $psql_timeout = IPC::Run::timer(60); +my ($mysession_stdin, $mysession_stdout, $mysession_stderr) = ('', '', ''); +my $mysession = IPC::Run::start( + [ + 'psql', '-X', '-qAt', '-v', 'ON_ERROR_STOP=1', '-f', '-', '-d', + $node_primary->connstr('postgres') + ], + '<', + \$mysession_stdin, + '>', + \$mysession_stdout, + '2>', + \$mysession_stderr, + $psql_timeout); + +# Write in transaction and get backend pid +$mysession_stdin .= q[ +BEGIN; +INSERT INTO tab VALUES(7); +SELECT $$value-7-inserted-into-tab$$; +]; +$mysession->pump until $mysession_stdout =~ /value-7-inserted-into-tab[\r\n]$/; +like($mysession_stdout, qr/value-7-inserted-into-tab/, + 'started write transaction in a session'); +$mysession_stdout = ''; +$mysession_stderr = ''; + +# Change to WAL prohibited +$node_primary->safe_psql('postgres', 'SELECT pg_prohibit_wal(true)'); +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), 'on', + 'server is changed to wal prohibited by another session'); + +# Try to commit open write transaction. +$mysession_stdin .= q[ +COMMIT; +]; +$mysession->pump; +like($mysession_stderr, qr/FATAL: WAL is now prohibited/, + 'session with open write transaction is terminated'); + +# Now stop the primary server in WAL prohibited state and take filesystem level +# backup and set up new server from it. +$node_primary->stop; +my $backup_name = 'my_backup'; +$node_primary->backup_fs_cold($backup_name); +my $node_standby = PostgresNode->new('standby'); +$node_standby->init_from_backup($node_primary, $backup_name); +$node_standby->start; + +# The primary server is stopped in wal prohibited state, the filesystem level +# copy also be in wal prohibited state +is($node_standby->safe_psql('postgres', $show_wal_prohibited_query), 'on', + 'new server created using backup of a stopped primary is also wal prohibited'); + +# Start Primary +$node_primary->start; + +# Set the new server as standby of primary. +# enable_streaming will create standby.signal file which will take out system +# from wal prohibited state. +$node_standby->enable_streaming($node_primary); +$node_standby->restart; + +# Check if the new server has been taken out from the wal prohibited state. +is($node_standby->safe_psql('postgres', $show_wal_prohibited_query), + 'off', 'new server as standby is no longer wal prohibited'); + +# Recovery server cannot be put into wal prohibited state. +$node_standby->psql('postgres', 'SELECT pg_prohibit_wal(true)', + stdout => \$stdout, stderr => \$stderr); +like($stderr, qr/cannot execute pg_prohibit_wal\(\) during recovery/, + 'standby server state cannot be changed to wal prohibited'); + +# Primary is still in wal prohibited state, the further insert will fail. +$node_primary->psql('postgres', 'INSERT INTO tab VALUES(6)', + stdout => \$stdout, stderr => \$stderr); +like($stderr, qr/cannot execute INSERT in a read-only transaction/, + 'primary server is wal prohibited, table insert is failed'); + +# Change primary to WAL permitted +$node_primary->safe_psql('postgres', 'SELECT pg_prohibit_wal(false)'); +is($node_primary->safe_psql('postgres', $show_wal_prohibited_query), + 'off', 'primary server is change to wal permitted'); + +# Insert data +$node_primary->safe_psql('postgres', 'INSERT INTO tab VALUES(6)'); +is($node_primary->safe_psql('postgres', 'SELECT count(i) FROM tab'), '7', + 'insert passed on primary'); + +# Wait for standbys to catch up +$node_primary->wait_for_catchup($node_standby, 'write'); +is($node_standby->safe_psql('postgres', 'SELECT count(i) FROM tab'), '7', + 'new insert replicated on standby as well'); +# +# Get latest checkpoint lsn from control file +# +sub get_latest_checkpoint_location +{ + my ($node) = @_; + my $data_dir = $node->data_dir; + my ($stdout, $stderr) = run_command([ 'pg_controldata', $data_dir ]); + my @control_data = split("\n", $stdout); + + my $latest_checkpoint_lsn = undef; + foreach (@control_data) + { + if ($_ =~ /^Latest checkpoint location:\s*(.*)$/mg) + { + $latest_checkpoint_lsn = $1; + last; + } + } + die "No latest checkpoint location in control file found\n" + unless defined($latest_checkpoint_lsn); + + return $latest_checkpoint_lsn; +} -- 2.18.0