v20240401-0005-use-copy_file_range-with-checksums.patch

text/x-patch

Filename: v20240401-0005-use-copy_file_range-with-checksums.patch
Type: text/x-patch
Part: 4
Message: Re: pg_combinebackup --copy-file-range

Patch

Format: format-patch
Series: patch v20240401-0005
Subject: use copy_file_range with checksums
File+
src/bin/pg_combinebackup/pg_combinebackup.c 29 0
src/bin/pg_combinebackup/reconstruct.c 45 25
From 6f21b1a635ec0f65a525238698d94953635ab52b Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sat, 30 Mar 2024 17:41:27 +0100
Subject: [PATCH v20240401 5/7] use copy_file_range with checksums

Use copy_file_range even when having to calculate checksum. Does
extra pg_pread() to read the data, but the data is still copies
using copy_file_range().
---
 src/bin/pg_combinebackup/pg_combinebackup.c | 29 +++++++++
 src/bin/pg_combinebackup/reconstruct.c      | 70 +++++++++++++--------
 2 files changed, 74 insertions(+), 25 deletions(-)

diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index 76a63d826a7..4f3d7747ce6 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -223,6 +223,35 @@ main(int argc, char *argv[])
 	if (opt.no_manifest)
 		opt.manifest_checksums = CHECKSUM_TYPE_NONE;
 
+	/* Check that the platform supports the requested copy method. */
+	if (opt.copy_method == COPY_METHOD_CLONE)
+	{
+#if (defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)) || \
+	(defined(__linux__) && defined(FICLONE))
+
+		if (opt.dry_run)
+			pg_log_debug("would use cloning to copy files");
+		else
+			pg_log_debug("will use cloning to copy files");
+
+#else
+		pg_fatal("file cloning not supported on this platform");
+#endif
+	}
+	else if (opt.copy_method == COPY_METHOD_COPY_FILE_RANGE)
+	{
+#if defined(HAVE_COPY_FILE_RANGE)
+
+		if (opt.dry_run)
+			pg_log_debug("would use copy_file_range to copy blocks");
+		else
+			pg_log_debug("will use copy_file_range to copy blocks");
+
+#else
+		pg_fatal("copy_file_range not supported on this platform");
+#endif
+	}
+
 	/* Read the server version from the final backup. */
 	version = read_pg_version_file(argv[argc - 1]);
 
diff --git a/src/bin/pg_combinebackup/reconstruct.c b/src/bin/pg_combinebackup/reconstruct.c
index f02c91a5d4e..e4081ddfcec 100644
--- a/src/bin/pg_combinebackup/reconstruct.c
+++ b/src/bin/pg_combinebackup/reconstruct.c
@@ -629,6 +629,9 @@ write_reconstructed_file(char *input_filename,
 		rfile	   *s = sourcemap[i];
 		int			wb;
 
+		bool		skip_page_read = false;
+		bool		use_copy_range = false;
+
 		/* Update accounting information. */
 		if (s == NULL)
 			++zero_blocks;
@@ -644,28 +647,23 @@ write_reconstructed_file(char *input_filename,
 			continue;
 
 		/*
-		 * If requested, copy the block using copy_file_range.
+		 * Do we need to actually read the souce block?
 		 *
-		 * We can'd do this if the block needs to be zero-filled or when we
-		 * need to update checksum.
+		 * We only need to read the block if we are to perform plain copy or
+		 * calculate the checksum. If the user requested copy_file_range, we
+		 * may be able to skip reading the block.
 		 */
-		if ((copy_method == COPY_METHOD_COPY_FILE_RANGE) &&
-			(s != NULL) && (checksum_ctx->type == CHECKSUM_TYPE_NONE))
-		{
-#if defined(HAVE_COPY_FILE_RANGE)
-			wb = copy_file_range(s->fd, &offsetmap[i], wfd, NULL, BLCKSZ, 0);
+		skip_page_read = ((copy_method == COPY_METHOD_COPY_FILE_RANGE) &&
+						  (checksum_ctx->type == CHECKSUM_TYPE_NONE));
 
-			if (wb < 0)
-				pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
-						 input_filename, output_filename);
-			else if (wb != BLCKSZ)
-				pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
-						 output_filename, wb, BLCKSZ);
-#else
-			pg_fatal("copy_file_range not supported on this platform");
-#endif
-			continue;
-		}
+		/*
+		 * Should we copy the block using copy_file_range?
+		 *
+		 * We can do that if the user requested that, and the block is not
+		 * going to be zero-filled.
+		 */
+		use_copy_range = ((copy_method == COPY_METHOD_COPY_FILE_RANGE) &&
+						  (s != NULL));
 
 		/* Read or zero-fill the block as appropriate. */
 		if (s == NULL)
@@ -676,7 +674,7 @@ write_reconstructed_file(char *input_filename,
 			 */
 			memset(buffer, 0, BLCKSZ);
 		}
-		else
+		else if (!skip_page_read)
 		{
 			int			rb;
 
@@ -707,17 +705,39 @@ write_reconstructed_file(char *input_filename,
 			}
 		}
 
-		/* Write out the block. */
-		if ((wb = write(wfd, buffer, BLCKSZ)) != BLCKSZ)
+		/*
+		 * If possible, copy the block using copy_file_range. If not possible
+		 * (not requested/supported, or the block is zero-filled), fallback to
+		 * the regular write.
+		 */
+		if (use_copy_range)
 		{
+			wb = copy_file_range(s->fd, &offsetmap[i], wfd, NULL, BLCKSZ, 0);
+
 			if (wb < 0)
-				pg_fatal("could not write file \"%s\": %m", output_filename);
-			else
+				pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
+						 input_filename, output_filename);
+			else if (wb != BLCKSZ)
 				pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
 						 output_filename, wb, BLCKSZ);
 		}
+		else
+		{
+			/* Write out the block. */
+			if ((wb = write(wfd, buffer, BLCKSZ)) != BLCKSZ)
+			{
+				if (wb < 0)
+					pg_fatal("could not write file \"%s\": %m", output_filename);
+				else
+					pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
+							 output_filename, wb, BLCKSZ);
+			}
+		}
 
-		/* Update the checksum computation. */
+		/*
+		 * Update the checksum computation (we must have read the page if we're
+		 * to calculate the checksum).
+		 */
 		if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
 			pg_fatal("could not update checksum of file \"%s\"",
 					 output_filename);
-- 
2.44.0