From 33102242590f980aad49ea4054ff1572740eb4f5 Mon Sep 17 00:00:00 2001
From: Osumi Takamichi <osumi.takamichi@fujitsu.com>
Date: Tue, 8 Dec 2020 02:28:46 +0000
Subject: [PATCH v02] Safeguard for archive recovery not to miss data

This disables the server to start up when it
detects WAL generated with wal_level=minimal during archive recovery.
The motivation of this patch is to protect user ends up with getting replica
that could miss data and getting the server to miss data in targeted recovery mode.

Author: Takamichi Osumi <osumi.takamichi@fujitsu.com>
Discussion: https://www.postgresql.org/message-id/OSBPR01MB4888CBE1DA08818FD2D90ED8EDF90%40OSBPR01MB4888.jpnprd01.prod.outlook.com
---
 src/backend/access/transam/xlog.c           |  2 +-
 src/test/recovery/t/022_archive_recovery.pl | 77 +++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 src/test/recovery/t/022_archive_recovery.pl

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7e81ce4..8bfccd0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6258,7 +6258,7 @@ CheckRequiredParameterValues(void)
 	 */
 	if (ArchiveRecoveryRequested && ControlFile->wal_level == WAL_LEVEL_MINIMAL)
 	{
-		ereport(WARNING,
+		ereport(FATAL,
 				(errmsg("WAL was generated with wal_level=minimal, data may be missing"),
 				 errhint("This happens if you temporarily set wal_level=minimal without taking a new base backup.")));
 	}
diff --git a/src/test/recovery/t/022_archive_recovery.pl b/src/test/recovery/t/022_archive_recovery.pl
new file mode 100644
index 0000000..6b1c7ed
--- /dev/null
+++ b/src/test/recovery/t/022_archive_recovery.pl
@@ -0,0 +1,77 @@
+# Prohibit archive recovery when the server detects WAL generated with wal_level=minimal
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 4;
+use Config;
+use Time::HiRes qw(usleep);
+
+# Initialize a node
+my $node = get_new_node('orig');
+my $backup_name = 'my_backup';
+my $replica_config = q[
+wal_level = replica
+archive_mode = on
+max_wal_senders = 10
+hot_standby = off
+];
+
+# Start up the server with wal_level = replica
+$node->init(has_archiving => 1);
+$node->append_conf('postgresql.conf', $replica_config);
+$node->start;
+
+# Check the wal_level and get a replica
+check_wal_level('replica', 'wal_level is replica at first');
+$node->backup($backup_name);
+
+# Change the wal_level from replica to minimal
+$node->append_conf(
+	'postgresql.conf', q[
+wal_level = minimal
+archive_mode = off
+max_wal_senders = 0
+]);
+$node->restart;
+check_wal_level('minimal', 'wal_level has become minimal');
+
+# Make the wal_level back to replica
+$node->append_conf('postgresql.conf', $replica_config);
+$node->restart;
+check_wal_level('replica', 'wal_level went back to replica again');
+$node->stop;
+
+# Execute an archive recovery, using the backup
+my $new_node = get_new_node('new_node');
+$new_node->init_from_backup(
+	$node, $backup_name,
+	has_restoring => 1, standby => 0);
+run_log(
+	[
+	 'pg_ctl',               '-D', $new_node->data_dir, '-l',
+	 $new_node->logfile, 'start'
+	]);
+
+# Wait up to 180s for postgres to terminate
+foreach my $i (0 .. 1800)
+{
+    last if !-f $new_node->data_dir . '/postmaster.pid';
+    usleep(100_000);
+}
+
+# Confirm that the archive recovery failed with an error intentionally
+my $logfile = slurp_file($new_node->logfile());
+ok( $logfile =~
+      qr/FATAL:  WAL was generated with wal_level=minimal, data may be missing/,
+    'Archive recovery with WAL generated during wal_level=minimal is a fatal error');
+
+sub check_wal_level
+{
+	my ($target_wal_level, $explanation) = @_;
+
+	is( $node->safe_psql(
+			'postgres', q{show wal_level}),
+        $target_wal_level,
+        $explanation);
+}
-- 
2.2.0

