summaryrefslogtreecommitdiff
path: root/src/bin/pg_rewind/RewindTest.pm
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_rewind/RewindTest.pm')
-rw-r--r--src/bin/pg_rewind/RewindTest.pm271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm
new file mode 100644
index 00000000000..0f8f4ca7ce0
--- /dev/null
+++ b/src/bin/pg_rewind/RewindTest.pm
@@ -0,0 +1,271 @@
+package RewindTest;
+
+# Test driver for pg_rewind. Each test consists of a cycle where a new cluster
+# is first created with initdb, and a streaming replication standby is set up
+# to follow the master. Then the master is shut down and the standby is
+# promoted, and finally pg_rewind is used to rewind the old master, using the
+# standby as the source.
+#
+# To run a test, the test script (in t/ subdirectory) calls the functions
+# in this module. These functions should be called in this sequence:
+#
+# 1. init_rewind_test - sets up log file etc.
+#
+# 2. setup_cluster - creates a PostgreSQL cluster that runs as the master
+#
+# 3. create_standby - runs pg_basebackup to initialize a standby server, and
+# sets it up to follow the master.
+#
+# 4. promote_standby - runs "pg_ctl promote" to promote the standby server.
+# The old master keeps running.
+#
+# 5. run_pg_rewind - stops the old master (if it's still running) and runs
+# pg_rewind to synchronize it with the now-promoted standby server.
+#
+# The test script can use the helper functions master_psql and standby_psql
+# to run psql against the master and standby servers, respectively. The
+# test script can also use the $connstr_master and $connstr_standby global
+# variables, which contain libpq connection strings for connecting to the
+# master and standby servers. The data directories are also available
+# in paths $test_master_datadir and $test_standby_datadir
+
+use TestLib;
+use Test::More;
+
+use File::Copy;
+use File::Path qw(remove_tree);
+use IPC::Run qw(run start);
+
+use Exporter 'import';
+our @EXPORT = qw(
+ $connstr_master
+ $connstr_standby
+ $test_master_datadir
+ $test_standby_datadir
+
+ append_to_file
+ master_psql
+ standby_psql
+ check_query
+
+ init_rewind_test
+ setup_cluster
+ create_standby
+ promote_standby
+ run_pg_rewind
+);
+
+
+# Adjust these paths for your environment
+my $testroot = "./tmp_check";
+$test_master_datadir="$testroot/data_master";
+$test_standby_datadir="$testroot/data_standby";
+
+mkdir $testroot;
+
+# Log files are created here
+mkdir "regress_log";
+
+# Define non-conflicting ports for both nodes.
+my $port_master=$ENV{PGPORT};
+my $port_standby=$port_master + 1;
+
+my $log_path;
+my $tempdir_short;
+
+$connstr_master="port=$port_master";
+$connstr_standby="port=$port_standby";
+
+$ENV{PGDATABASE} = "postgres";
+
+sub master_psql
+{
+ my $cmd = shift;
+
+ system_or_bail("psql -q --no-psqlrc -d $connstr_master -c \"$cmd\"");
+}
+
+sub standby_psql
+{
+ my $cmd = shift;
+
+ system_or_bail("psql -q --no-psqlrc -d $connstr_standby -c \"$cmd\"");
+}
+
+# Run a query against the master, and check that the output matches what's
+# expected
+sub check_query
+{
+ my ($query, $expected_stdout, $test_name) = @_;
+ my ($stdout, $stderr);
+
+ # we want just the output, no formatting
+ my $result = run ['psql', '-q', '-A', '-t', '--no-psqlrc',
+ '-d', $connstr_master,
+ '-c' , $query],
+ '>', \$stdout, '2>', \$stderr;
+ # We don't use ok() for the exit code and stderr, because we want this
+ # check to be just a single test.
+ if (!$result) {
+ fail ("$test_name: psql exit code");
+ } elsif ($stderr ne '') {
+ diag $stderr;
+ fail ("$test_name: psql no stderr");
+ } else {
+ is ($stdout, $expected_stdout, "$test_name: query result matches");
+ }
+}
+
+sub append_to_file
+{
+ my($filename, $str) = @_;
+
+ open my $fh, ">>", $filename or die "could not open file $filename";
+ print $fh $str;
+ close $fh;
+}
+
+sub init_rewind_test
+{
+ ($testname, $test_mode) = @_;
+
+ $log_path="regress_log/pg_rewind_log_${testname}_${test_mode}";
+
+ remove_tree $log_path;
+}
+
+sub setup_cluster
+{
+ $tempdir_short = tempdir_short;
+
+ # Initialize master, data checksums are mandatory
+ remove_tree($test_master_datadir);
+ standard_initdb($test_master_datadir);
+
+ # Custom parameters for master's postgresql.conf
+ append_to_file("$test_master_datadir/postgresql.conf", qq(
+wal_level = hot_standby
+max_wal_senders = 2
+wal_keep_segments = 20
+max_wal_size = 200MB
+shared_buffers = 1MB
+wal_log_hints = on
+hot_standby = on
+autovacuum = off
+max_connections = 10
+));
+
+ # Accept replication connections on master
+ append_to_file("$test_master_datadir/pg_hba.conf", qq(
+local replication all trust
+));
+
+ system_or_bail("pg_ctl -w -D $test_master_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_master\" start >>$log_path 2>&1");
+
+ #### Now run the test-specific parts to initialize the master before setting
+ # up standby
+ $ENV{PGHOST} = $tempdir_short;
+}
+
+sub create_standby
+{
+ # Set up standby with necessary parameter
+ remove_tree $test_standby_datadir;
+
+ # Base backup is taken with xlog files included
+ system_or_bail("pg_basebackup -D $test_standby_datadir -p $port_master -x >>$log_path 2>&1");
+ append_to_file("$test_standby_datadir/recovery.conf", qq(
+primary_conninfo='$connstr_master'
+standby_mode=on
+recovery_target_timeline='latest'
+));
+
+ # Start standby
+ system_or_bail("pg_ctl -w -D $test_standby_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_standby\" start >>$log_path 2>&1");
+
+ # sleep a bit to make sure the standby has caught up.
+ sleep 1;
+}
+
+sub promote_standby
+{
+ #### Now run the test-specific parts to run after standby has been started
+ # up standby
+
+ # Now promote slave and insert some new data on master, this will put
+ # the master out-of-sync with the standby.
+ system_or_bail("pg_ctl -w -D $test_standby_datadir promote >>$log_path 2>&1");
+ sleep 1;
+}
+
+sub run_pg_rewind
+{
+ # Stop the master and be ready to perform the rewind
+ system_or_bail("pg_ctl -w -D $test_master_datadir stop -m fast >>$log_path 2>&1");
+
+ # At this point, the rewind processing is ready to run.
+ # We now have a very simple scenario with a few diverged WAL record.
+ # The real testing begins really now with a bifurcation of the possible
+ # scenarios that pg_rewind supports.
+
+ # Keep a temporary postgresql.conf for master node or it would be
+ # overwritten during the rewind.
+ copy("$test_master_datadir/postgresql.conf", "$testroot/master-postgresql.conf.tmp");
+ # Now run pg_rewind
+ if ($test_mode == "local")
+ {
+ # Do rewind using a local pgdata as source
+ # Stop the master and be ready to perform the rewind
+ system_or_bail("pg_ctl -w -D $test_standby_datadir stop -m fast >>$log_path 2>&1");
+ my $result =
+ run(['./pg_rewind',
+ "--debug",
+ "--source-pgdata=$test_standby_datadir",
+ "--target-pgdata=$test_master_datadir"],
+ '>>', $log_path, '2>&1');
+ ok ($result, 'pg_rewind local');
+ }
+ elsif ($test_mode == "remote")
+ {
+ # Do rewind using a remote connection as source
+ my $result =
+ run(['./pg_rewind',
+ "--source-server=\"port=$port_standby dbname=postgres\"",
+ "--target-pgdata=$test_master_datadir"],
+ '>>', $log_path, '2>&1');
+ ok ($result, 'pg_rewind remote');
+ } else {
+ # Cannot come here normally
+ die("Incorrect test mode specified");
+ }
+
+ # Now move back postgresql.conf with old settings
+ move("$testroot/master-postgresql.conf.tmp", "$test_master_datadir/postgresql.conf");
+
+ # Plug-in rewound node to the now-promoted standby node
+ append_to_file("$test_master_datadir/recovery.conf", qq(
+primary_conninfo='port=$port_standby'
+standby_mode=on
+recovery_target_timeline='latest'
+));
+
+ # Restart the master to check that rewind went correctly
+ system_or_bail("pg_ctl -w -D $test_master_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_master\" start >>$log_path 2>&1");
+
+ #### Now run the test-specific parts to check the result
+}
+
+# Clean up after the test. Stop both servers, if they're still running.
+END
+{
+ my $save_rc = $?;
+ if ($test_master_datadir)
+ {
+ system "pg_ctl -D $test_master_datadir -s -m immediate stop 2> /dev/null";
+ }
+ if ($test_standby_datadir)
+ {
+ system "pg_ctl -D $test_standby_datadir -s -m immediate stop 2> /dev/null";
+ }
+ $? = $save_rc;
+}