From 6d534f6d1f1b8a0a375a57cde9479543efb87f14 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Fri, 9 Aug 2024 18:23:02 +0300
Subject: [PATCH 4/4] Update minRecoveryPoint when no rewind is required

Alternatively, perhaps just copying the history file would be enough?
---
 src/bin/pg_rewind/pg_rewind.c                 | 40 ++++++++++++++++++-
 src/bin/pg_rewind/t/010_target_is_ancestor.pl | 21 ++++++----
 2 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index 101e92b549..e2767ba9ec 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -77,6 +77,9 @@ bool		do_sync = true;
 static bool restore_wal = false;
 DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
 
+static char *sourceHistfile;
+static char *sourceHistfileName;
+
 /* Target history */
 TimeLineHistoryEntry *targetHistory;
 int			targetNentries;
@@ -372,7 +375,7 @@ main(int argc, char **argv)
 	 */
 	if (target_tli == source_tli)
 	{
-		pg_log_info("source and target cluster are on the same timeline");
+		pg_log_info("no rewind required, source and target cluster are on the same timeline");
 		rewind_needed = false;
 		target_wal_endrec = 0;
 	}
@@ -431,12 +434,39 @@ main(int argc, char **argv)
 		/*
 		 * Check for the possibility that the target is in fact a direct
 		 * ancestor of the source. In that case, there is no divergent history
-		 * in the target that needs rewinding.
+		 * in the target that needs rewinding, but we still update the
+		 * target's minRecoveryPoint, so that when you start it up, it will
+		 * recover to that timeline. (Otherwise, if it has more WAL locally in
+		 * pg_wal or available from the archive, it might choose to follow
+		 * some other timeline.)
 		 */
 		if (target_wal_endrec > divergerec)
 			rewind_needed = true;
 		else
+		{
+			ControlFileData ControlFile_new;
+
 			rewind_needed = false;
+
+			memcpy(&ControlFile_new, &ControlFile_target, sizeof(ControlFileData));
+			ControlFile_new.minRecoveryPoint = 	Max(ControlFile_source.minRecoveryPoint,
+													ControlFile_source.checkPoint);
+			ControlFile_new.minRecoveryPointTLI = source_tli;
+			pg_log_info("no rewind required, updating target to source's timeline",
+						LSN_FORMAT_ARGS(ControlFile_new.minRecoveryPoint),
+						ControlFile_new.minRecoveryPointTLI);
+			if (!dry_run)
+				update_controlfile(datadir_target, &ControlFile_new, do_sync);
+
+			/*
+			 * Make the timeline history file available to the target. Avoids
+			 * having to fetch it immediately at startup, and ensures that it
+			 * has the same idea of what the timeline is as we did.
+			 */
+			open_target_file(sourceHistfileName, true);
+			write_target_range(sourceHistfile, 0, strlen(sourceHistfile));
+			close_target_file();
+		}
 	}
 
 	if (!rewind_needed)
@@ -872,6 +902,12 @@ getTimelineHistory(TimeLineID tli, bool is_source, int *nentries)
 			histfile = slurpFile(datadir_target, path, NULL);
 
 		history = rewind_parseTimeLineHistory(histfile, tli, nentries);
+		if (is_source)
+		{
+			sourceHistfileName = pstrdup(path);
+			sourceHistfile = histfile;
+		}
+		else
 		pg_free(histfile);
 	}
 
diff --git a/src/bin/pg_rewind/t/010_target_is_ancestor.pl b/src/bin/pg_rewind/t/010_target_is_ancestor.pl
index 541581a4e8..15aaa60f2d 100644
--- a/src/bin/pg_rewind/t/010_target_is_ancestor.pl
+++ b/src/bin/pg_rewind/t/010_target_is_ancestor.pl
@@ -67,8 +67,8 @@ $orig_node->backup_fs_cold('backup_C');
 my $source_node = $node_2;
 my $source_pgdata = $source_node->data_dir;
 
-# Create a new node from a backup, and run pg_rewind to rewind it as a
-# follower of $source_node
+# Create a new node from a backup, and rewind it as a follower of
+# $source_node
 sub rewind_from_backup
 {
 	my ($backup, $expected_stderr) = @_;
@@ -90,22 +90,29 @@ sub rewind_from_backup
 		],
 		0,
 		[],
-		[$expected_stderr],
+		$expected_stderr,
 		"pg_rewind on node restored from $backup");
 
 	# Now move back postgresql.conf with old settings
 	move("$tmp_folder/postgresql.conf.tmp", "$target_pgdata/postgresql.conf");
 
+	# Make the more recent WAL from timeline 1 available to the
+	# restored server.  It doesn't need it, the point of this is to
+	# test that the server ignores this extra WAL.
+	copy($orig_node->data_dir . "/pg_wal/000000010000000000000001",
+	  $target_node->data_dir . "/pg_wal/000000010000000000000001")
+	  || die "copying 000000010000000000000001: $!";
+
 	# Configure it to connect to the new primary
 	my $node_2_connstr = $node_2->connstr();
 	$target_node->append_conf(
 		'postgresql.conf', qq(
 primary_conninfo = '$node_2_connstr'
 ));
+	$node_2->start;
 	$target_node->set_standby_mode();
 	$target_node->start;
 
-	$node_2->start;
 	$node_2->wait_for_catchup($target_node->name);
 	my $result =
 	  $target_node->safe_psql('postgres', "SELECT * FROM test_tab");
@@ -119,8 +126,8 @@ in node_2),
 }
 
 rewind_from_backup('backup_C',
-	qr/pg_rewind: rewinding from last common checkpoint at .* on timeline 1/);
-rewind_from_backup('backup_B', qr/pg_rewind: no rewind required/);
-rewind_from_backup('backup_A', qr/pg_rewind: no rewind required/);
+	[qr/pg_rewind: rewinding from last common checkpoint at .* on timeline 1/]);
+rewind_from_backup('backup_B', [qr/no rewind required, updating target to source's timeline/]);
+rewind_from_backup('backup_A', [qr/no rewind required, updating target to source's timeline/]);
 
 done_testing();
-- 
2.39.2

