On Fri, Feb 5, 2016 at 4:17 AM, Michael Paquier wrote:
> Thanks for your enthusiasm. Now, to do an auto-critic of my patch:
> +       if ($params{allows_streaming})
> +       {
> +               print $conf "wal_level = hot_standby\n";
> +               print $conf "max_wal_senders = 5\n";
> +               print $conf "wal_keep_segments = 20\n";
> +               print $conf "max_wal_size = 128MB\n";
> +               print $conf "shared_buffers = 1MB\n";
> +               print $conf "wal_log_hints = on\n";
> +               print $conf "hot_standby = on\n";
> +       }
> This could have more thoughts, particularly for wal_log_hints which is
> not used all the time, I think that we'd actually want to complete
> that with an optional hash of parameter/values that get appended at
> the end of the configuration file, then pass wal_log_hints in the
> tests where it is needed. The default set of parameter is maybe fine
> if done this way, still wal_keep_segments could be removed.

At the end I have refrained from doing that, and refactoring
setup_clus...@rewindtest.pm to use the new option allows_streaming,
simplifying a bit the code. The introduction of allows_streaming could
be done in a separate patch, though it did not seem worth the
complication when hacking at that. The split is simple, though.

> +# Tets for timeline switch
> +# Encure that a standby is able to follow a newly-promoted standby
> Two typos in two lines.

Fixed.

I also found an issue with the use of application_name causing test
001 to fail, bug squashed on the way. The generation of paths for
archive_command and restore_command was incorrect on Windows. Those
need to use two backslashes (to detect correctly files) and need to be
double-quoted (to avoid errors with command copy should a space be
included in the path). I have added as well a new subcommand in
vcregress.pl called recoverycheck where one can run the recovery test
suite on Windows using MSVC.

Attached are rebased patches, split into 3 parts doing the following:
- 0001, fix default configuration of MSVC builds ignoring TAP tests
- 0002, add a promote routine in PostgresNode.pm. pg_rewind's tests
can make immediate use of that.
- 0003, the actual test suite.
This is registered in CF 2016-03 as well for further consideration.
-- 
Michael
From 0231866094b793ccd7c6941a19d5565c3d5811b6 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Mon, 21 Dec 2015 16:28:34 +0900
Subject: [PATCH 1/3] Fix default configuration of MSVC builds ignoring TAP
 tests

MSVC build scripts use a flag to track if TAP tests are supported or not
but this was not configured correctly. By default, like the other build
types using ./configure, this is disabled.
---
 src/tools/msvc/Solution.pm       |  1 +
 src/tools/msvc/config_default.pl | 27 ++++++++++++++-------------
 2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index ac116b7..c5a43f9 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -643,6 +643,7 @@ sub GetFakeConfigure
 	$cfg .= ' --enable-integer-datetimes'
 	  if ($self->{options}->{integer_datetimes});
 	$cfg .= ' --enable-nls' if ($self->{options}->{nls});
+	$cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests});
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index b9f2ff4..e50be7e 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 
 our $config = {
-	asserts => 0,    # --enable-cassert
+	asserts  => 0,    # --enable-cassert
 	  # integer_datetimes=>1,   # --enable-integer-datetimes - on is now default
 	  # float4byval=>1,         # --disable-float4-byval, on by default
 
@@ -13,18 +13,19 @@ our $config = {
 	# blocksize => 8,         # --with-blocksize, 8kB by default
 	# wal_blocksize => 8,     # --with-wal-blocksize, 8kB by default
 	# wal_segsize => 16,      # --with-wal-segsize, 16MB by default
-	ldap     => 1,        # --with-ldap
-	extraver => undef,    # --with-extra-version=<string>
-	nls      => undef,    # --enable-nls=<path>
-	tcl      => undef,    # --with-tls=<path>
-	perl     => undef,    # --with-perl
-	python   => undef,    # --with-python=<path>
-	openssl  => undef,    # --with-openssl=<path>
-	uuid     => undef,    # --with-ossp-uuid
-	xml      => undef,    # --with-libxml=<path>
-	xslt     => undef,    # --with-libxslt=<path>
-	iconv    => undef,    # (not in configure, path to iconv)
-	zlib     => undef     # --with-zlib=<path>
+	ldap      => 1,        # --with-ldap
+	extraver  => undef,    # --with-extra-version=<string>
+	nls       => undef,    # --enable-nls=<path>
+	tap_tests => undef,    # --enable-tap-tests
+	tcl       => undef,    # --with-tls=<path>
+	perl      => undef,    # --with-perl
+	python    => undef,    # --with-python=<path>
+	openssl   => undef,    # --with-openssl=<path>
+	uuid      => undef,    # --with-ossp-uuid
+	xml       => undef,    # --with-libxml=<path>
+	xslt      => undef,    # --with-libxslt=<path>
+	iconv     => undef,    # (not in configure, path to iconv)
+	zlib      => undef     # --with-zlib=<path>
 };
 
 1;
-- 
2.7.1

From f866f5a211ba1fc3c34b4272c9b752ced90684ae Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Wed, 17 Feb 2016 15:21:32 +0900
Subject: [PATCH 2/3] Add routine for node promotion in PostgresNode

This is useful for tests to trigger a promotion on a node via pg_ctl,
and pg_rewind can make immediate use of it.
---
 src/bin/pg_rewind/RewindTest.pm |  2 +-
 src/test/perl/PostgresNode.pm   | 12 ++++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm
index 68834cd..e204830 100644
--- a/src/bin/pg_rewind/RewindTest.pm
+++ b/src/bin/pg_rewind/RewindTest.pm
@@ -177,7 +177,7 @@ sub promote_standby
 	# Now promote slave and insert some new data on master, this will put
 	# the master out-of-sync with the standby. Wait until the standby is
 	# out of recovery mode, and is ready to accept read-write connections.
-	system_or_bail('pg_ctl', '-w', '-D', $node_standby->data_dir, 'promote');
+	$node_standby->promote;
 	$node_standby->poll_query_until('postgres',
 		"SELECT NOT pg_is_in_recovery()")
 	  or die "Timed out while waiting for promotion of standby";
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 2ab9aee..5a57e31 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -294,6 +294,18 @@ sub restart
 	$self->_update_pid;
 }
 
+sub promote
+{
+	my ($self)  = @_;
+	my $port    = $self->port;
+	my $pgdata  = $self->data_dir;
+	my $logfile = $self->logfile;
+	my $name    = $self->name;
+	print "### Promoting node \"$name\"\n";
+	TestLib::system_log('pg_ctl', '-D', $pgdata, '-w', '-l', $logfile,
+						'promote');
+}
+
 sub _update_pid
 {
 	my $self = shift;
-- 
2.7.1

From 5959031af49436262788fede65c3fcb60fb343b7 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Wed, 17 Feb 2016 21:50:19 +0900
Subject: [PATCH 3/3] Add recovery test suite

This includes basic tests maipulating standbys, be they archiving or
streaming nodes, and some basic sanity checks around them. PostgresNode
is extended with a couple of routines allowing to set up WAL archiving,
WAL streaming or WAL restore on a node, as well as a commodity routine
to allow a promotion.
---
 doc/src/sgml/install-windows.sgml           |   4 +-
 src/bin/pg_rewind/RewindTest.pm             |  17 +---
 src/test/Makefile                           |   2 +-
 src/test/perl/PostgresNode.pm               | 126 +++++++++++++++++++++++++++-
 src/test/recovery/.gitignore                |   3 +
 src/test/recovery/Makefile                  |  17 ++++
 src/test/recovery/README                    |  19 +++++
 src/test/recovery/t/001_stream_rep.pl       |  62 ++++++++++++++
 src/test/recovery/t/002_archiving.pl        |  46 ++++++++++
 src/test/recovery/t/003_recovery_targets.pl | 125 +++++++++++++++++++++++++++
 src/test/recovery/t/004_timeline_switch.pl  |  67 +++++++++++++++
 src/test/recovery/t/005_replay_delay.pl     |  43 ++++++++++
 src/tools/msvc/config_default.pl            |   2 +-
 src/tools/msvc/vcregress.pl                 |  13 ++-
 14 files changed, 524 insertions(+), 22 deletions(-)
 create mode 100644 src/test/recovery/.gitignore
 create mode 100644 src/test/recovery/Makefile
 create mode 100644 src/test/recovery/README
 create mode 100644 src/test/recovery/t/001_stream_rep.pl
 create mode 100644 src/test/recovery/t/002_archiving.pl
 create mode 100644 src/test/recovery/t/003_recovery_targets.pl
 create mode 100644 src/test/recovery/t/004_timeline_switch.pl
 create mode 100644 src/test/recovery/t/005_replay_delay.pl

diff --git a/doc/src/sgml/install-windows.sgml b/doc/src/sgml/install-windows.sgml
index ba60a6b..8e87b70 100644
--- a/doc/src/sgml/install-windows.sgml
+++ b/doc/src/sgml/install-windows.sgml
@@ -440,6 +440,7 @@ $ENV{CONFIG}="Debug";
 <userinput>vcregress ecpgcheck</userinput>
 <userinput>vcregress isolationcheck</userinput>
 <userinput>vcregress bincheck</userinput>
+<userinput>vcregress recoverycheck</userinput>
 <userinput>vcregress upgradecheck</userinput>
 </screen>
 
@@ -455,7 +456,8 @@ $ENV{CONFIG}="Debug";
 
   <para>
    Running the regression tests on client programs, with "vcregress bincheck",
-   requires an additional Perl module to be installed:
+   or on recovery tests, with "vceregress recoverycheck" requires an additional
+   Perl module to be installed:
    <variablelist>
     <varlistentry>
      <term><productname>IPC::Run</productname></term>
diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm
index e204830..bda0516 100644
--- a/src/bin/pg_rewind/RewindTest.pm
+++ b/src/bin/pg_rewind/RewindTest.pm
@@ -114,24 +114,9 @@ sub check_query
 
 sub setup_cluster
 {
-
 	# Initialize master, data checksums are mandatory
 	$node_master = get_new_node('master');
-	$node_master->init;
-
-	# Custom parameters for master's postgresql.conf
-	$node_master->append_conf(
-		"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
-));
+	$node_master->init(allows_streaming => 1);
 }
 
 sub start_master
diff --git a/src/test/Makefile b/src/test/Makefile
index b713c2c..7f7754f 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -12,7 +12,7 @@ subdir = src/test
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = regress isolation modules
+SUBDIRS = regress isolation modules recovery
 
 # We don't build or execute examples/, locale/, or thread/ by default,
 # but we do want "make clean" etc to recurse into them.  Likewise for ssl/,
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 5a57e31..82a0d30 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -171,6 +171,10 @@ sub init
 
 	$params{hba_permit_replication} = 1
 	  if (!defined($params{hba_permit_replication}));
+	$params{has_archiving} = 0
+	  if (!defined($params{has_archiving}));
+	$params{allows_streaming} = 0
+	  if (!defined($params{allows_streaming}));
 
 	mkdir $self->backup_dir;
 	mkdir $self->archive_dir;
@@ -183,6 +187,20 @@ sub init
 	print $conf "fsync = off\n";
 	print $conf "log_statement = all\n";
 	print $conf "port = $port\n";
+
+	if ($params{allows_streaming})
+	{
+		print $conf "wal_level = hot_standby\n";
+		print $conf "max_wal_senders = 5\n";
+		print $conf "wal_keep_segments = 20\n";
+		print $conf "max_wal_size = 128MB\n";
+		print $conf "shared_buffers = 1MB\n";
+		print $conf "autovacuum = off\n";
+		print $conf "wal_log_hints = on\n";
+		print $conf "hot_standby = on\n";
+		print $conf "max_connections = 10\n";
+	}
+
 	if ($TestLib::windows_os)
 	{
 		print $conf "listen_addresses = '$host'\n";
@@ -195,6 +213,7 @@ sub init
 	close $conf;
 
 	$self->set_replication_conf if ($params{hba_permit_replication});
+	$self->enable_archiving if ($params{has_archiving});
 }
 
 sub append_conf
@@ -220,12 +239,19 @@ sub backup
 
 sub init_from_backup
 {
-	my ($self, $root_node, $backup_name) = @_;
+	my ($self, $root_node, $backup_name, %params) = @_;
 	my $backup_path = $root_node->backup_dir . '/' . $backup_name;
 	my $port        = $self->port;
 	my $node_name   = $self->name;
 	my $root_name   = $root_node->name;
 
+	$params{hba_permit_replication} = 1
+	   if (!defined($params{hba_permit_replication}));
+	$params{has_streaming} = 0
+	   if (!defined($params{has_streaming}));
+	$params{has_restoring} = 0
+	   if (!defined($params{has_restoring}));
+
 	print
 "# Initializing node \"$node_name\" from backup \"$backup_name\" of node \"$root_name\"\n";
 	die "Backup \"$backup_name\" does not exist at $backup_path"
@@ -245,7 +271,10 @@ sub init_from_backup
 		qq(
 port = $port
 ));
-	$self->set_replication_conf;
+
+	$self->set_replication_conf if ($params{hba_permit_replication});
+	$self->enable_restoring($root_node) if ($params{has_restoring});
+	$self->enable_streaming($root_node) if ($params{has_streaming});
 }
 
 sub start
@@ -306,6 +335,99 @@ sub promote
 						'promote');
 }
 
+#
+# Set of routines for replication and recovery
+#
+sub enable_streaming
+{
+	my ($self, $root_node)  = @_;
+	my $root_connstr = $root_node->connstr;
+	my $name    = $self->name;
+	my $pgdata  = $self->data_dir;
+	my $port    = $self->port;
+
+	print "### Enabling streaming replication for node in $pgdata with port $port\n";
+	$self->append_conf('recovery.conf', qq(
+primary_conninfo='$root_connstr application_name=$name'
+standby_mode=on
+));
+}
+
+sub enable_restoring
+{
+	my ($self, $root_node)  = @_;
+	my $path = $root_node->archive_dir;
+	my $pgdata  = $self->data_dir;
+	my $port    = $self->port;
+
+	print "### Enabling restoring for node in $pgdata with port $port\n";
+
+	# On Windows, the path specified in the restore command needs to use
+	# double back-slashes to work properly and to be able to detect properly
+	# the file targetted by the copy command, so the directory value used
+	# in this routine, using only one back-slash, need to be properly changed
+	# first. Paths also need to be double-quoted to prevent failures where
+	# the path contains spaces.
+	$path =~ s{\\}{\\\\}g if ($TestLib::windows_os);
+	my $copy_command = $TestLib::windows_os ?
+		qq{copy "$path\\\\%f" "%p"} :
+		qq{cp $path/%f %p};
+
+	$self->append_conf('recovery.conf', qq(
+restore_command = '$copy_command'
+standby_mode = on
+));
+}
+
+sub enable_archiving
+{
+	my ($self) = @_;
+	my $path = $self->archive_dir;
+	my $pgdata  = $self->data_dir;
+	my $port    = $self->port;
+
+	print "### Enabling archiving for node in $pgdata with port $port\n";
+
+	# On Windows, the path specified in the restore command needs to use
+	# double back-slashes to work properly and to be able to detect properly
+	# the file targetted by the copy command, so the directory value used
+	# in this routine, using only one back-slash, need to be properly changed
+	# first. Paths also need to be double-quoted to prevent failures where
+	# the path contains spaces.
+	$path =~ s{\\}{\\\\}g if ($TestLib::windows_os);
+	my $copy_command = $TestLib::windows_os ?
+		qq{copy "%p" "$path\\\\%f"} :
+		qq{cp %p $path/%f};
+
+	# Enable archive_mode and archive_command on node
+	$self->append_conf('postgresql.conf', qq(
+archive_mode = on
+archive_command = '$copy_command'
+));
+}
+
+# Wait until a node is able to accept queries. Useful when putting a node
+# in recovery and wait for it to be able to work particularly on slow
+# machines.
+sub wait_for_access
+{
+	my ($self) = @_;
+	my $max_attempts = 30;
+	my $attempts     = 0;
+	while ($attempts < $max_attempts)
+	{
+		if (run_log(['pg_isready', '-d', $self->connstr('postgres')]))
+		{
+			return 1;
+		}
+
+		# Wait a second before retrying.
+		sleep 1;
+		$attempts++;
+	}
+	return 0;
+}
+
 sub _update_pid
 {
 	my $self = shift;
diff --git a/src/test/recovery/.gitignore b/src/test/recovery/.gitignore
new file mode 100644
index 0000000..499fa7d
--- /dev/null
+++ b/src/test/recovery/.gitignore
@@ -0,0 +1,3 @@
+# Generated by test suite
+/regress_log/
+/tmp_check/
diff --git a/src/test/recovery/Makefile b/src/test/recovery/Makefile
new file mode 100644
index 0000000..16c063a
--- /dev/null
+++ b/src/test/recovery/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/recovery
+#
+# Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/recovery/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/recovery
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+check:
+	$(prove_check)
diff --git a/src/test/recovery/README b/src/test/recovery/README
new file mode 100644
index 0000000..20b98e0
--- /dev/null
+++ b/src/test/recovery/README
@@ -0,0 +1,19 @@
+src/test/recovery/README
+
+Regression tests for recovery and replication
+=============================================
+
+This directory contains a test suite for recovery and replication,
+testing mainly the interactions of recovery.conf with cluster
+instances by providing a simple set of routines that can be used
+to define a custom cluster for a test, including backup, archiving,
+and streaming configuration.
+
+Running the tests
+=================
+
+    make check
+
+NOTE: This creates a temporary installation, and some tests may
+create one or multiple nodes, be they master or standby(s) for the
+purpose of the tests.
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
new file mode 100644
index 0000000..3ed9be3
--- /dev/null
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -0,0 +1,62 @@
+# Minimal test testing streaming replication
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 4;
+
+# Initialize master node
+my $node_master = get_new_node('master');
+$node_master->init(allows_streaming => 1);
+$node_master->start;
+my $backup_name = 'my_backup';
+
+# Take backup
+$node_master->backup($backup_name);
+
+# Create streaming standby linking to master
+my $node_standby_1 = get_new_node('standby_1');
+$node_standby_1->init_from_backup($node_master, $backup_name,
+								  has_streaming => 1);
+$node_standby_1->start;
+
+# Take backup of standby 1 (not mandatory, but useful to check if
+# pg_basebackup works on a standby).
+$node_standby_1->backup($backup_name);
+
+# Create second standby node linking to standby 1
+my $node_standby_2 = get_new_node('standby_2');
+$node_standby_2->init_from_backup($node_standby_1, $backup_name,
+								  has_streaming => 1);
+$node_standby_2->start;
+
+# Create some content on master and check its presence in standby 1
+$node_master->psql('postgres', "CREATE TABLE tab_int AS SELECT generate_series(1,1002) AS a");
+
+# Wait for standbys to catch up
+my $applname_1 = $node_standby_1->name;
+my $applname_2 = $node_standby_2->name;
+my $caughtup_query = "SELECT pg_current_xlog_location() = write_location FROM pg_stat_replication WHERE application_name = '$applname_1';";
+$node_master->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby 1 to catch up";
+$caughtup_query = "SELECT pg_last_xlog_replay_location() = write_location FROM pg_stat_replication WHERE application_name = '$applname_2';";
+$node_standby_1->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby 2 to catch up";
+
+my $result = $node_standby_1->psql('postgres', "SELECT count(*) FROM tab_int");
+print "standby 1: $result\n";
+is($result, qq(1002), 'check streamed content on standby 1');
+
+$result =  $node_standby_2->psql('postgres', "SELECT count(*) FROM tab_int");
+print "standby 2: $result\n";
+is($result, qq(1002), 'check streamed content on standby 2');
+
+# Check that only READ-only queries can run on standbys
+$node_standby_1->command_fails(['psql', '-A', '-t',  '--no-psqlrc',
+	'-d', $node_standby_1->connstr, '-c',
+    "INSERT INTO tab_int VALUES (1)"],
+	'Read-only queries on standby 1');
+$node_standby_2->command_fails(['psql', '-A', '-t',  '--no-psqlrc',
+	'-d', $node_standby_2->connstr, '-c',
+    "INSERT INTO tab_int VALUES (1)"],
+	'Read-only queries on standby 2');
diff --git a/src/test/recovery/t/002_archiving.pl b/src/test/recovery/t/002_archiving.pl
new file mode 100644
index 0000000..930125c
--- /dev/null
+++ b/src/test/recovery/t/002_archiving.pl
@@ -0,0 +1,46 @@
+# test for archiving with warm standby
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 1;
+use File::Copy;
+
+# Initialize master node, doing archives
+my $node_master = get_new_node('master');
+$node_master->init(has_archiving => 1,
+				   allows_streaming => 1);
+my $backup_name = 'my_backup';
+
+# Start it
+$node_master->start;
+
+# Take backup for slave
+$node_master->backup($backup_name);
+
+# Initialize standby node from backup, fetching WAL from archives
+my $node_standby = get_new_node('standby');
+$node_standby->init_from_backup($node_master, $backup_name,
+								has_restoring => 1);
+$node_standby->append_conf('postgresql.conf', qq(
+wal_retrieve_retry_interval = '100ms'
+));
+$node_standby->start;
+
+# Create some content on master
+$node_master->psql('postgres', "CREATE TABLE tab_int AS SELECT generate_series(1,1000) AS a");
+my $current_lsn = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+
+# Force archiving of WAL file to make it present on master
+$node_master->psql('postgres', "SELECT pg_switch_xlog()");
+
+# Add some more content, it should not be present on standby
+$node_master->psql('postgres', "INSERT INTO tab_int VALUES (generate_series(1001,2000))");
+
+# Wait until necessary replay has been done on standby
+my $caughtup_query = "SELECT '$current_lsn'::pg_lsn <= pg_last_xlog_replay_location()";
+$node_standby->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby to catch up";
+
+my $result = $node_standby->psql('postgres', "SELECT count(*) FROM tab_int");
+is($result, qq(1000), 'check content from archives');
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
new file mode 100644
index 0000000..293603a
--- /dev/null
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -0,0 +1,125 @@
+# Test for recovery targets: name, timestamp, XID
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 7;
+
+# Create and test a standby from given backup, with a certain
+# recovery target.
+sub test_recovery_standby
+{
+	my $test_name = shift;
+	my $node_name = shift;
+	my $node_master = shift;
+	my $recovery_params = shift;
+	my $num_rows = shift;
+	my $until_lsn = shift;
+
+	my $node_standby = get_new_node($node_name);
+	$node_standby->init_from_backup($node_master, 'my_backup',
+									has_restoring => 1);
+
+	foreach my $param_item (@$recovery_params)
+	{
+		$node_standby->append_conf('recovery.conf',
+					   qq($param_item
+));
+	}
+
+	$node_standby->start;
+
+	# Wait until standby has replayed enough data
+	my $caughtup_query = "SELECT '$until_lsn'::pg_lsn <= pg_last_xlog_replay_location()";
+	$node_standby->poll_query_until('postgres', $caughtup_query)
+		or die "Timed out while waiting for standby to catch up";
+
+	# Create some content on master and check its presence in standby
+	my $result = $node_standby->psql('postgres', "SELECT count(*) FROM tab_int");
+	is($result, qq($num_rows), "check standby content for $test_name");
+
+	# Stop standby node
+	$node_standby->teardown_node;
+}
+
+# Initialize master node
+my $node_master = get_new_node('master');
+$node_master->init(has_archiving => 1, allows_streaming => 1);
+
+# Start it
+$node_master->start;
+
+# Create data before taking the backup, aimed at testing
+# recovery_target = 'immediate'
+$node_master->psql('postgres', "CREATE TABLE tab_int AS SELECT generate_series(1,1000) AS a");
+my $lsn1 = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+
+# Take backup from which all operations will be run
+$node_master->backup('my_backup');
+
+# Insert some data with used as a replay reference, with a recovery
+# target TXID.
+$node_master->psql('postgres', "INSERT INTO tab_int VALUES (generate_series(1001,2000))");
+my $recovery_txid = $node_master->psql('postgres', "SELECT txid_current()");
+my $lsn2 = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+
+# More data, with recovery target timestamp
+$node_master->psql('postgres', "INSERT INTO tab_int VALUES (generate_series(2001,3000))");
+my $recovery_time = $node_master->psql('postgres', "SELECT now()");
+my $lsn3 = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+
+# Even more data, this time with a recovery target name
+$node_master->psql('postgres',
+	"INSERT INTO tab_int VALUES (generate_series(3001,4000))");
+my $recovery_name = "my_target";
+my $lsn4 = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+$node_master->psql('postgres', "SELECT pg_create_restore_point('$recovery_name'");
+
+# Force archiving of WAL file
+$node_master->psql('postgres', "SELECT pg_switch_xlog()");
+
+# Test recovery targets
+my @recovery_params = ( "recovery_target = 'immediate'" );
+test_recovery_standby('immediate target', 'standby_1', $node_master,
+					  \@recovery_params,
+					  "1000", $lsn1);
+@recovery_params = ( "recovery_target_xid = '$recovery_txid'" );
+test_recovery_standby('XID', 'standby_2', $node_master,
+					  \@recovery_params,
+					  "2000", $lsn2);
+@recovery_params = ( "recovery_target_time = '$recovery_time'" );
+test_recovery_standby('Time', 'standby_3', $node_master,
+					  \@recovery_params,
+					  "3000", $lsn3);
+@recovery_params = ( "recovery_target_name = '$recovery_name'" );
+test_recovery_standby('Name', 'standby_4', $node_master,
+					  \@recovery_params,
+					  "4000", $lsn4);
+
+# Multiple targets
+# Last entry has priority (note that an array respects the order of items
+# not hashes).
+@recovery_params = (
+	"recovery_target_name = '$recovery_name'",
+	"recovery_target_xid  = '$recovery_txid'",
+	"recovery_target_time = '$recovery_time'"
+);
+test_recovery_standby('Name + XID + Time', 'standby_5', $node_master,
+					  \@recovery_params,
+					  "3000", $lsn3);
+@recovery_params = (
+	"recovery_target_time = '$recovery_time'",
+	"recovery_target_name = '$recovery_name'",
+	"recovery_target_xid  = '$recovery_txid'"
+);
+test_recovery_standby('Time + Name + XID', 'standby_6', $node_master,
+					  \@recovery_params,
+					  "2000", $lsn2);
+@recovery_params = (
+	"recovery_target_xid  = '$recovery_txid'",
+	"recovery_target_time = '$recovery_time'",
+	"recovery_target_name = '$recovery_name'"
+);
+test_recovery_standby('XID + Time + Name', 'standby_7', $node_master,
+					  \@recovery_params,
+					  "4000", $lsn4);
diff --git a/src/test/recovery/t/004_timeline_switch.pl b/src/test/recovery/t/004_timeline_switch.pl
new file mode 100644
index 0000000..dc08ec1
--- /dev/null
+++ b/src/test/recovery/t/004_timeline_switch.pl
@@ -0,0 +1,67 @@
+# Test for timeline switch
+# Ensure that a standby is able to follow a newly-promoted standby
+# on a new timeline.
+use strict;
+use warnings;
+use File::Path qw(remove_tree);
+use PostgresNode;
+use TestLib;
+use Test::More tests => 1;
+
+$ENV{PGDATABASE} = 'postgres';
+
+# Initialize master node
+my $node_master = get_new_node('master');
+$node_master->init(allows_streaming => 1);
+$node_master->start;
+
+# Take backup
+my $backup_name = 'my_backup';
+$node_master->backup($backup_name);
+
+# Create two standbys linking to it
+my $node_standby_1 = get_new_node('standby_1');
+$node_standby_1->init_from_backup($node_master, $backup_name,
+								  has_streaming => 1);
+$node_standby_1->start;
+my $node_standby_2 = get_new_node('standby_2');
+$node_standby_2->init_from_backup($node_master, $backup_name,
+								  has_streaming => 1);
+$node_standby_2->start;
+
+# Create some content on master
+$node_master->psql('postgres',
+	"CREATE TABLE tab_int AS SELECT generate_series(1,1000) AS a");
+my $until_lsn = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+
+# Wait until standby has replayed enough data on standby 1
+my $caughtup_query = "SELECT '$until_lsn'::pg_lsn <= pg_last_xlog_replay_location()";
+$node_standby_1->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby to catch up";
+
+# Stop and remove master, and promote standby 1, switching it to a new timeline
+$node_master->teardown_node;
+$node_standby_1->promote;
+
+# Switch standby 2 to replay from standby 1
+remove_tree($node_standby_2->data_dir . '/recovery.conf');
+my $connstr_1 = $node_standby_1->connstr;
+$node_standby_2->append_conf('recovery.conf', qq(
+primary_conninfo='$connstr_1'
+standby_mode=on
+recovery_target_timeline='latest'
+));
+$node_standby_2->restart;
+
+# Insert some data in standby 1 and check its presence in standby 2
+# to ensure that the timeline switch has been done. Standby 1 needs
+# to exit recovery first before moving on with the test.
+$node_standby_1->poll_query_until('postgres', "SELECT pg_is_in_recovery() <> true");
+$node_standby_1->psql('postgres', "INSERT INTO tab_int VALUES (generate_series(1001,2000))");
+$until_lsn = $node_standby_1->psql('postgres', "SELECT pg_current_xlog_location();");
+$caughtup_query = "SELECT '$until_lsn'::pg_lsn <= pg_last_xlog_replay_location()";
+$node_standby_2->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby to catch up";
+
+my $result = $node_standby_2->psql('postgres', "SELECT count(*) FROM tab_int");
+is($result, qq(2000), 'check content of standby 2');
diff --git a/src/test/recovery/t/005_replay_delay.pl b/src/test/recovery/t/005_replay_delay.pl
new file mode 100644
index 0000000..2d8c690
--- /dev/null
+++ b/src/test/recovery/t/005_replay_delay.pl
@@ -0,0 +1,43 @@
+# Checks for recovery_min_apply_delay
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 2;
+
+# Initialize master node
+my $node_master = get_new_node('master');
+$node_master->init(allows_streaming => 1);
+$node_master->start;
+
+# And some content
+$node_master->psql('postgres', "CREATE TABLE tab_int AS SELECT generate_series(1,10) AS a");
+
+# Take backup
+my $backup_name = 'my_backup';
+$node_master->backup($backup_name);
+
+# Create streaming standby from backup
+my $node_standby = get_new_node('standby');
+$node_standby->init_from_backup($node_master, $backup_name,
+								has_streaming => 1);
+$node_standby->append_conf('recovery.conf', qq(
+recovery_min_apply_delay = '2s'
+));
+$node_standby->start;
+
+# Make new content on master and check its presence in standby
+# depending on the delay of 2s applied above.
+$node_master->psql('postgres', "INSERT INTO tab_int VALUES (generate_series(11,20))");
+sleep 1;
+# Here we should have only 10 rows
+my $result = $node_standby->psql('postgres', "SELECT count(*) FROM tab_int");
+is($result, qq(10), 'check content with delay of 1s');
+
+# Now wait for replay to complete on standby
+my $until_lsn = $node_master->psql('postgres', "SELECT pg_current_xlog_location();");
+my $caughtup_query = "SELECT '$until_lsn'::pg_lsn <= pg_last_xlog_replay_location()";
+$node_standby->poll_query_until('postgres', $caughtup_query)
+	or die "Timed out while waiting for standby to catch up";
+$result = $node_standby->psql('postgres', "SELECT count(*) FROM tab_int");
+is($result, qq(20), 'check content with delay of 2s');
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index e50be7e..636beea 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -16,7 +16,7 @@ our $config = {
 	ldap      => 1,        # --with-ldap
 	extraver  => undef,    # --with-extra-version=<string>
 	nls       => undef,    # --enable-nls=<path>
-	tap_tests => undef,    # --enable-tap-tests
+	tap_tests => 1,    # --enable-tap-tests
 	tcl       => undef,    # --with-tls=<path>
 	perl      => undef,    # --with-perl
 	python    => undef,    # --with-python=<path>
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index df1348b..3d14544 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -34,7 +34,7 @@ if (-e "src/tools/msvc/buildenv.pl")
 
 my $what = shift || "";
 if ($what =~
-/^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|bincheck)$/i
+/^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|bincheck|recoverycheck)$/i
   )
 {
 	$what = uc $what;
@@ -89,6 +89,7 @@ my %command = (
 	MODULESCHECK   => \&modulescheck,
 	ISOLATIONCHECK => \&isolationcheck,
 	BINCHECK       => \&bincheck,
+	RECOVERYCHECK  => \&recoverycheck,
 	UPGRADECHECK   => \&upgradecheck,);
 
 my $proc = $command{$what};
@@ -360,6 +361,16 @@ sub modulescheck
 	exit $mstat if $mstat;
 }
 
+sub recoverycheck
+{
+	InstallTemp();
+
+	my $mstat = 0;
+	my $dir = "$topdir/src/test/recovery";
+	my $status = tap_check($dir);
+	exit $status if $status;
+}
+
 # Run "initdb", then reconfigure authentication.
 sub standard_initdb
 {
-- 
2.7.1

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to