From f6f4c94c3c291f8b473e7f9048b42c0115331b8a Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Sun, 23 Aug 2026 12:15:15 +0300
Subject: [PATCH v1] Make test WAL archiving publish files atomically

---
 src/test/perl/PostgreSQL/Test/Cluster.pm | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 920d831be9e..da0499ad14b 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -1541,10 +1541,25 @@ sub enable_archiving
 	# first. Paths also need to be double-quoted to prevent failures where
 	# the path contains spaces.
 	$path =~ s{\\}{\\\\}g if ($PostgreSQL::Test::Utils::windows_os);
-	my $copy_command =
-	  $PostgreSQL::Test::Utils::windows_os
-	  ? qq{copy "%p" "$path\\\\%f"}
-	  : qq{cp "%p" "$path/%f"};
+	# Copy to a temporary file in the archive directory, then publish it under
+	# the name expected by restore_command.  In particular, don't leave a
+	# partially-copied file visible if the archive command is interrupted.
+	# Generate the suffix here so that all commands use exactly the same name.
+	my $temp_suffix = sprintf("%08x", int(rand(0x7fffffff)));
+	my $copy_command;
+
+	if ($PostgreSQL::Test::Utils::windows_os)
+	{
+		my $temp_path = "$path\\\\.%f.tmp.$temp_suffix";
+		$copy_command = qq{copy /Y "%p" "$temp_path"}
+		  . qq{ && move /Y "$temp_path" "$path\\\\%f"};
+	}
+	else
+	{
+		my $temp_path = "$path/.%f.tmp.$temp_suffix.\$\$";
+		$copy_command = qq{cp "%p" "$temp_path"}
+		  . qq{ && mv -f "$temp_path" "$path/%f"};
+	}
 
 	# Enable archive_mode and archive_command on node
 	$self->append_conf(
-- 
That's all, folks. May the source be with you.

