From e7bce46439e3041df1bc9607e69aa9535751de92 Mon Sep 17 00:00:00 2001 From: Peter Weldon Date: Sun, 27 Oct 2024 17:00:04 +0000 Subject: [PATCH] Use copy_file_range if available to allow COW filesystem optimization. Use case is for a near instantaneous CREATE DATABASE .. WITH TEMPLATE .. STRATEGY file_copy --- src/backend/storage/file/copydir.c | 70 ++++++++++++++++++++++++++++++ src/include/storage/copydir.h | 1 + 2 files changed, 71 insertions(+) diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c index d4fbe542077f1..5c58b123b871b 100644 --- a/src/backend/storage/file/copydir.c +++ b/src/backend/storage/file/copydir.c @@ -19,13 +19,19 @@ #include "postgres.h" #include +#include #include #include "common/file_utils.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/copydir.h" + +#include +#include + #include "storage/fd.h" +#include "pg_config.h" /* * copydir: copy a directory @@ -71,7 +77,11 @@ copydir(const char *fromdir, const char *todir, bool recurse) copydir(fromfile, tofile, true); } else if (xlde_type == PGFILETYPE_REG) +#if defined(HAVE_COPY_FILE_RANGE) + copy_file_reflink(fromfile, tofile); +#else copy_file(fromfile, tofile); +#endif } FreeDir(xldir); @@ -214,3 +224,63 @@ copy_file(const char *fromfile, const char *tofile) pfree(buffer); } + +/* + * copy one file using copy_file_range to give filesystem a chance to do COW optimization + */ +void +copy_file_reflink(const char *fromfile, const char *tofile) +{ +#if defined(HAVE_COPY_FILE_RANGE) + int srcfd; + int dstfd; + ssize_t nbytes; + + /* + * Open the files + */ + srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY); + + if (srcfd < 0) + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not open file \"%s\": %m", fromfile)); + + dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY); + + if (dstfd < 0) + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not create file \"%s\": %m", tofile)); + + /* + * Do the data copying. + */ + do + { + /* If we got a cancel signal during the copy of the file, quit */ + CHECK_FOR_INTERRUPTS(); + + pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ); + nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, SSIZE_MAX, 0); + pgstat_report_wait_end(); + if (nbytes < 0) + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not copy file \"%s\": %m", fromfile)); + } + while (nbytes > 0); + + if (CloseTransientFile(dstfd) != 0) + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", tofile)); + + if (CloseTransientFile(srcfd) != 0) + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", fromfile)); +#else + pg_fatal("copy_file_range not available on this platform"); +#endif +} diff --git a/src/include/storage/copydir.h b/src/include/storage/copydir.h index a25e258f4797e..fb831f363b05e 100644 --- a/src/include/storage/copydir.h +++ b/src/include/storage/copydir.h @@ -15,5 +15,6 @@ extern void copydir(const char *fromdir, const char *todir, bool recurse); extern void copy_file(const char *fromfile, const char *tofile); +extern void copy_file_reflink(const char *fromfile, const char *tofile); #endif /* COPYDIR_H */