From 0e42397f42b370798461cbf3358185b520329f9f Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 3 Aug 2015 15:32:06 +0300 Subject: [PATCH] Fix pg_rewind when pg_xlog is a symlink. pg_xlog is often a symlink, typically to a different filesystem. Don't get confused and comlain about by that, and just always pretend that it's a normal directory, even if it's really a symlink. Also add a test case for this. Backpatch to 9.5. --- src/bin/pg_rewind/RewindTest.pm | 14 ++-- src/bin/pg_rewind/filemap.c | 16 ++++- src/bin/pg_rewind/t/001_basic.pl | 1 + src/bin/pg_rewind/t/002_databases.pl | 1 + src/bin/pg_rewind/t/003_extrafiles.pl | 1 + src/bin/pg_rewind/t/004_pg_xlog_symlink.pl | 79 ++++++++++++++++++++++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 src/bin/pg_rewind/t/004_pg_xlog_symlink.pl diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm index 25f243b49e..b66ff0dc2a 100644 --- a/src/bin/pg_rewind/RewindTest.pm +++ b/src/bin/pg_rewind/RewindTest.pm @@ -13,16 +13,18 @@ package RewindTest; # # 2. setup_cluster - creates a PostgreSQL cluster that runs as the master # -# 3. create_standby - runs pg_basebackup to initialize a standby server, and +# 3. start_master - starts the master server +# +# 4. 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. +# 5. 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 +# 6. 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. # -# 6. clean_rewind_test - stops both servers used in the test, if they're +# 7. clean_rewind_test - stops both servers used in the test, if they're # still running. # # The test script can use the helper functions master_psql and standby_psql @@ -56,6 +58,7 @@ our @EXPORT = qw( init_rewind_test setup_cluster + start_master create_standby promote_standby run_pg_rewind @@ -182,7 +185,10 @@ max_connections = 10 # Accept replication connections on master configure_hba_for_replication $test_master_datadir; +} +sub start_master +{ system_or_bail('pg_ctl' , '-w', '-D' , $test_master_datadir, '-l', "$log_path/master.log", diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c index 05eff68185..fb26d09316 100644 --- a/src/bin/pg_rewind/filemap.c +++ b/src/bin/pg_rewind/filemap.c @@ -78,6 +78,14 @@ process_source_file(const char *path, file_type_t type, size_t newsize, strcmp(path, "postmaster.opts") == 0) return; + /* + * Pretend that pg_xlog is a directory, even if it's really a symlink. + * We don't want to mess with the symlink itself, nor complain if it's a + * symlink in source but not in target or vice versa. + */ + if (strcmp(path, "pg_xlog") == 0 && type == FILE_TYPE_SYMLINK) + type = FILE_TYPE_DIRECTORY; + /* * Skip temporary files, .../pgsql_tmp/... and .../pgsql_tmp.* in source. * This has the effect that all temporary files in the destination will be @@ -112,7 +120,7 @@ process_source_file(const char *path, file_type_t type, size_t newsize, switch (type) { case FILE_TYPE_DIRECTORY: - if (exists && !S_ISDIR(statbuf.st_mode)) + if (exists && !S_ISDIR(statbuf.st_mode) && strcmp(path, "pg_xlog") != 0) { /* it's a directory in source, but not in target. Strange.. */ pg_fatal("\"%s\" is not a directory\n", localpath); @@ -285,6 +293,12 @@ process_target_file(const char *path, file_type_t type, size_t oldsize, strcmp(path, "postmaster.opts") == 0) return; + /* + * Like in process_source_file, pretend that xlog is always a directory. + */ + if (strcmp(path, "pg_xlog") == 0 && type == FILE_TYPE_SYMLINK) + type = FILE_TYPE_DIRECTORY; + key.path = (char *) path; key_ptr = &key; exists = (bsearch(&key_ptr, map->array, map->narray, sizeof(file_entry_t *), diff --git a/src/bin/pg_rewind/t/001_basic.pl b/src/bin/pg_rewind/t/001_basic.pl index be7d887bb7..1764b17c90 100644 --- a/src/bin/pg_rewind/t/001_basic.pl +++ b/src/bin/pg_rewind/t/001_basic.pl @@ -10,6 +10,7 @@ sub run_test my $test_mode = shift; RewindTest::setup_cluster(); + RewindTest::start_master(); # Create a test table and insert a row in master. master_psql("CREATE TABLE tbl1 (d text)"); diff --git a/src/bin/pg_rewind/t/002_databases.pl b/src/bin/pg_rewind/t/002_databases.pl index b0b007a763..f10899d440 100644 --- a/src/bin/pg_rewind/t/002_databases.pl +++ b/src/bin/pg_rewind/t/002_databases.pl @@ -10,6 +10,7 @@ sub run_test my $test_mode = shift; RewindTest::setup_cluster(); + RewindTest::start_master(); # Create a database in master. master_psql('CREATE DATABASE inmaster'); diff --git a/src/bin/pg_rewind/t/003_extrafiles.pl b/src/bin/pg_rewind/t/003_extrafiles.pl index 0cd0ac4d56..d317f53186 100644 --- a/src/bin/pg_rewind/t/003_extrafiles.pl +++ b/src/bin/pg_rewind/t/003_extrafiles.pl @@ -15,6 +15,7 @@ sub run_test my $test_mode = shift; RewindTest::setup_cluster(); + RewindTest::start_master(); my $test_master_datadir = $RewindTest::test_master_datadir; diff --git a/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl new file mode 100644 index 0000000000..0830b5a330 --- /dev/null +++ b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl @@ -0,0 +1,79 @@ +# +# Test pg_rewind when the target's pg_xlog directory is a symlink. +# +use strict; +use warnings; +use File::Copy; +use File::Path qw(remove_tree); +use TestLib; +use Test::More; +if ($windows_os) +{ + plan skip_all => 'symlinks not supported on Windows'; + exit; +} +else +{ + plan tests => 4; +} + +use RewindTest; + +sub run_test +{ + my $test_mode = shift; + + my $master_xlogdir = "$tmp_check/xlog_master"; + + remove_tree($master_xlogdir); + RewindTest::setup_cluster(); + + # turn pg_xlog into a symlink + print("moving $test_master_datadir/pg_xlog to $master_xlogdir\n"); + move("$test_master_datadir/pg_xlog", $master_xlogdir) or die; + symlink($master_xlogdir, "$test_master_datadir/pg_xlog") or die; + + RewindTest::start_master(); + + # Create a test table and insert a row in master. + master_psql("CREATE TABLE tbl1 (d text)"); + master_psql("INSERT INTO tbl1 VALUES ('in master')"); + + master_psql("CHECKPOINT"); + + RewindTest::create_standby(); + + # Insert additional data on master that will be replicated to standby + master_psql("INSERT INTO tbl1 values ('in master, before promotion')"); + + master_psql('CHECKPOINT'); + + RewindTest::promote_standby(); + + # Insert a row in the old master. This causes the master and standby + # to have "diverged", it's no longer possible to just apply the + # standy's logs over master directory - you need to rewind. + master_psql("INSERT INTO tbl1 VALUES ('in master, after promotion')"); + + # Also insert a new row in the standby, which won't be present in the + # old master. + standby_psql("INSERT INTO tbl1 VALUES ('in standby, after promotion')"); + + RewindTest::run_pg_rewind($test_mode); + + check_query( + 'SELECT * FROM tbl1', + qq(in master +in master, before promotion +in standby, after promotion +), + 'table content'); + + RewindTest::clean_rewind_test(); +} + +# Run the test in both modes +run_test('local'); +run_test('remote'); + +exit(0);