1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +03:00

Add pg_rewind, for re-synchronizing a master server after failback.

Earlier versions of this tool were available (and still are) on github.

Thanks to Michael Paquier, Alvaro Herrera, Peter Eisentraut, Amit Kapila,
and Satoshi Nagayasu for review.
This commit is contained in:
Heikki Linnakangas
2015-03-23 19:47:52 +02:00
parent 87cec51d3a
commit 61081e75c6
29 changed files with 4141 additions and 2 deletions

View File

@ -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;
}