Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3a769d8

Browse files
committed
pg_upgrade: Allow use of file cloning
Add another transfer mode --clone to pg_upgrade (besides the existing --link and the default copy), using special file cloning calls. This makes the file transfer faster and more space efficient, achieving speed similar to --link mode without the associated drawbacks. On Linux, file cloning is supported on Btrfs and XFS (if formatted with reflink support). On macOS, file cloning is supported on APFS. Reviewed-by: Michael Paquier <michael@paquier.xyz>
1 parent 5f32b29 commit 3a769d8

File tree

9 files changed

+181
-25
lines changed

9 files changed

+181
-25
lines changed

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15130,7 +15130,7 @@ fi
1513015130
LIBS_including_readline="$LIBS"
1513115131
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
1513215132

15133-
for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l
15133+
for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l
1513415134
do :
1513515135
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1513615136
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

configure.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,7 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
16021602
AC_CHECK_FUNCS(m4_normalize([
16031603
cbrt
16041604
clock_gettime
1605+
copyfile
16051606
fdatasync
16061607
getifaddrs
16071608
getpeerucred

doc/src/sgml/ref/pgupgrade.sgml

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,28 @@
182182
<listitem><para>display version information, then exit</para></listitem>
183183
</varlistentry>
184184

185+
<varlistentry>
186+
<term><option>--clone</option></term>
187+
<listitem>
188+
<para>
189+
Use efficient file cloning (also known as <quote>reflinks</quote> on
190+
some systems) instead of copying files to the new cluster. This can
191+
result in near-instantaneous copying of the data files, giving the
192+
speed advantages of <option>-k</option>/<option>--link</option> while
193+
leaving the old cluster untouched.
194+
</para>
195+
196+
<para>
197+
File cloning is only supported on some operating systems and file
198+
systems. If it is selected but not supported, the
199+
<application>pg_upgrade</application> run will error. At present, it
200+
is supported on Linux (kernel 4.5 or later) with Btrfs and XFS (on
201+
file systems created with reflink support, which is not the default
202+
for XFS at this writing), and on macOS with APFS.
203+
</para>
204+
</listitem>
205+
</varlistentry>
206+
185207
<varlistentry>
186208
<term><option>-?</option></term>
187209
<term><option>--help</option></term>
@@ -340,7 +362,7 @@ NET STOP postgresql-&majorversion;
340362
Always run the <application>pg_upgrade</application> binary of the new server, not the old one.
341363
<application>pg_upgrade</application> requires the specification of the old and new cluster's
342364
data and executable (<filename>bin</filename>) directories. You can also specify
343-
user and port values, and whether you want the data files linked
365+
user and port values, and whether you want the data files linked or cloned
344366
instead of the default copy behavior.
345367
</para>
346368

@@ -351,8 +373,12 @@ NET STOP postgresql-&majorversion;
351373
once you start the new cluster after the upgrade. Link mode also
352374
requires that the old and new cluster data directories be in the
353375
same file system. (Tablespaces and <filename>pg_wal</filename> can be on
354-
different file systems.) See <literal>pg_upgrade --help</literal> for a full
355-
list of options.
376+
different file systems.)
377+
The clone mode provides the same speed and disk space advantages but will
378+
not leave the old cluster unusable after the upgrade. The clone mode
379+
also requires that the old and new data directories be in the same file
380+
system. The clone mode is only available on certain operating systems
381+
and file systems.
356382
</para>
357383

358384
<para>
@@ -388,8 +414,9 @@ pg_upgrade.exe
388414
to perform only the checks, even if the old server is still
389415
running. <command>pg_upgrade --check</command> will also outline any
390416
manual adjustments you will need to make after the upgrade. If you
391-
are going to be using link mode, you should use the <option>--link</option>
392-
option with <option>--check</option> to enable link-mode-specific checks.
417+
are going to be using link or clone mode, you should use the option
418+
<option>--link</option> or <option>--clone</option> with
419+
<option>--check</option> to enable mode-specific checks.
393420
<command>pg_upgrade</command> requires write permission in the current directory.
394421
</para>
395422

@@ -722,7 +749,8 @@ psql --username=postgres --file=script.sql postgres
722749

723750
<para>
724751
If you want to use link mode and you do not want your old cluster
725-
to be modified when the new cluster is started, make a copy of the
752+
to be modified when the new cluster is started, consider using the clone mode.
753+
If that is not available, make a copy of the
726754
old cluster and upgrade that in link mode. To make a valid copy
727755
of the old cluster, use <command>rsync</command> to create a dirty
728756
copy of the old cluster while the server is running, then shut down

src/bin/pg_upgrade/check.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,17 @@ check_new_cluster(void)
149149

150150
check_loadable_libraries();
151151

152-
if (user_opts.transfer_mode == TRANSFER_MODE_LINK)
153-
check_hard_link();
152+
switch (user_opts.transfer_mode)
153+
{
154+
case TRANSFER_MODE_CLONE:
155+
check_file_clone();
156+
break;
157+
case TRANSFER_MODE_COPY:
158+
break;
159+
case TRANSFER_MODE_LINK:
160+
check_hard_link();
161+
break;
162+
}
154163

155164
check_is_install_user(&new_cluster);
156165

src/bin/pg_upgrade/file.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,61 @@
1818

1919
#include <sys/stat.h>
2020
#include <fcntl.h>
21+
#ifdef HAVE_COPYFILE
22+
#include <copyfile.h>
23+
#endif
24+
#ifdef __linux__
25+
#include <sys/ioctl.h>
26+
#include <linux/fs.h>
27+
#endif
2128

2229

2330
#ifdef WIN32
2431
static int win32_pghardlink(const char *src, const char *dst);
2532
#endif
2633

2734

35+
/*
36+
* cloneFile()
37+
*
38+
* Clones/reflinks a relation file from src to dst.
39+
*
40+
* schemaName/relName are relation's SQL name (used for error messages only).
41+
*/
42+
void
43+
cloneFile(const char *src, const char *dst,
44+
const char *schemaName, const char *relName)
45+
{
46+
#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
47+
if (copyfile(src, dst, NULL, COPYFILE_CLONE_FORCE) < 0)
48+
pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
49+
schemaName, relName, src, dst, strerror(errno));
50+
#elif defined(__linux__) && defined(FICLONE)
51+
int src_fd;
52+
int dest_fd;
53+
54+
if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
55+
pg_fatal("error while cloning relation \"%s.%s\": could not open file \"%s\": %s\n",
56+
schemaName, relName, src, strerror(errno));
57+
58+
if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
59+
pg_file_create_mode)) < 0)
60+
pg_fatal("error while cloning relation \"%s.%s\": could not create file \"%s\": %s\n",
61+
schemaName, relName, dst, strerror(errno));
62+
63+
if (ioctl(dest_fd, FICLONE, src_fd) < 0)
64+
{
65+
unlink(dst);
66+
pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
67+
schemaName, relName, src, dst, strerror(errno));
68+
}
69+
70+
close(src_fd);
71+
close(dest_fd);
72+
#endif
73+
}
74+
75+
2876
/*
2977
* copyFile()
3078
*
@@ -270,6 +318,48 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
270318
close(src_fd);
271319
}
272320

321+
void
322+
check_file_clone(void)
323+
{
324+
char existing_file[MAXPGPATH];
325+
char new_link_file[MAXPGPATH];
326+
327+
snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
328+
snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.clonetest", new_cluster.pgdata);
329+
unlink(new_link_file); /* might fail */
330+
331+
#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
332+
if (copyfile(existing_file, new_link_file, NULL, COPYFILE_CLONE_FORCE) < 0)
333+
pg_fatal("could not clone file between old and new data directories: %s\n",
334+
strerror(errno));
335+
#elif defined(__linux__) && defined(FICLONE)
336+
{
337+
int src_fd;
338+
int dest_fd;
339+
340+
if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
341+
pg_fatal("could not open file \"%s\": %s\n",
342+
existing_file, strerror(errno));
343+
344+
if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
345+
pg_file_create_mode)) < 0)
346+
pg_fatal("could not create file \"%s\": %s\n",
347+
new_link_file, strerror(errno));
348+
349+
if (ioctl(dest_fd, FICLONE, src_fd) < 0)
350+
pg_fatal("could not clone file between old and new data directories: %s\n",
351+
strerror(errno));
352+
353+
close(src_fd);
354+
close(dest_fd);
355+
}
356+
#else
357+
pg_fatal("file cloning not supported on this platform\n");
358+
#endif
359+
360+
unlink(new_link_file);
361+
}
362+
273363
void
274364
check_hard_link(void)
275365
{

src/bin/pg_upgrade/option.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ parseCommandLine(int argc, char *argv[])
5353
{"retain", no_argument, NULL, 'r'},
5454
{"jobs", required_argument, NULL, 'j'},
5555
{"verbose", no_argument, NULL, 'v'},
56+
{"clone", no_argument, NULL, 1},
57+
5658
{NULL, 0, NULL, 0}
5759
};
5860
int option; /* Command line option */
@@ -203,6 +205,10 @@ parseCommandLine(int argc, char *argv[])
203205
log_opts.verbose = true;
204206
break;
205207

208+
case 1:
209+
user_opts.transfer_mode = TRANSFER_MODE_CLONE;
210+
break;
211+
206212
default:
207213
pg_fatal("Try \"%s --help\" for more information.\n",
208214
os_info.progname);
@@ -293,6 +299,7 @@ usage(void)
293299
printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user);
294300
printf(_(" -v, --verbose enable verbose internal logging\n"));
295301
printf(_(" -V, --version display version information, then exit\n"));
302+
printf(_(" --clone clone instead of copying files to new cluster\n"));
296303
printf(_(" -?, --help show this help, then exit\n"));
297304
printf(_("\n"
298305
"Before running pg_upgrade you must:\n"

src/bin/pg_upgrade/pg_upgrade.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ typedef struct
230230
} ControlData;
231231

232232
/*
233-
* Enumeration to denote link modes
233+
* Enumeration to denote transfer modes
234234
*/
235235
typedef enum
236236
{
237+
TRANSFER_MODE_CLONE,
237238
TRANSFER_MODE_COPY,
238239
TRANSFER_MODE_LINK
239240
} transferMode;
@@ -372,12 +373,15 @@ bool pid_lock_file_exists(const char *datadir);
372373

373374
/* file.c */
374375

376+
void cloneFile(const char *src, const char *dst,
377+
const char *schemaName, const char *relName);
375378
void copyFile(const char *src, const char *dst,
376379
const char *schemaName, const char *relName);
377380
void linkFile(const char *src, const char *dst,
378381
const char *schemaName, const char *relName);
379382
void rewriteVisibilityMap(const char *fromfile, const char *tofile,
380383
const char *schemaName, const char *relName);
384+
void check_file_clone(void);
381385
void check_hard_link(void);
382386

383387
/* fopen_priv() is no longer different from fopen() */

src/bin/pg_upgrade/relfilenode.c

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,18 @@ void
3030
transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
3131
char *old_pgdata, char *new_pgdata)
3232
{
33-
if (user_opts.transfer_mode == TRANSFER_MODE_LINK)
34-
pg_log(PG_REPORT, "Linking user relation files\n");
35-
else
36-
pg_log(PG_REPORT, "Copying user relation files\n");
33+
switch (user_opts.transfer_mode)
34+
{
35+
case TRANSFER_MODE_CLONE:
36+
pg_log(PG_REPORT, "Cloning user relation files\n");
37+
break;
38+
case TRANSFER_MODE_COPY:
39+
pg_log(PG_REPORT, "Copying user relation files\n");
40+
break;
41+
case TRANSFER_MODE_LINK:
42+
pg_log(PG_REPORT, "Linking user relation files\n");
43+
break;
44+
}
3745

3846
/*
3947
* Transferring files by tablespace is tricky because a single database
@@ -250,17 +258,23 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
250258
old_file, new_file);
251259
rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
252260
}
253-
else if (user_opts.transfer_mode == TRANSFER_MODE_COPY)
254-
{
255-
pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"\n",
256-
old_file, new_file);
257-
copyFile(old_file, new_file, map->nspname, map->relname);
258-
}
259261
else
260-
{
261-
pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"\n",
262-
old_file, new_file);
263-
linkFile(old_file, new_file, map->nspname, map->relname);
264-
}
262+
switch (user_opts.transfer_mode)
263+
{
264+
case TRANSFER_MODE_CLONE:
265+
pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"\n",
266+
old_file, new_file);
267+
cloneFile(old_file, new_file, map->nspname, map->relname);
268+
break;
269+
case TRANSFER_MODE_COPY:
270+
pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"\n",
271+
old_file, new_file);
272+
copyFile(old_file, new_file, map->nspname, map->relname);
273+
break;
274+
case TRANSFER_MODE_LINK:
275+
pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"\n",
276+
old_file, new_file);
277+
linkFile(old_file, new_file, map->nspname, map->relname);
278+
}
265279
}
266280
}

src/include/pg_config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@
114114
/* Define to 1 if your compiler handles computed gotos. */
115115
#undef HAVE_COMPUTED_GOTO
116116

117+
/* Define to 1 if you have the `copyfile' function. */
118+
#undef HAVE_COPYFILE
119+
117120
/* Define to 1 if you have the <crtdefs.h> header file. */
118121
#undef HAVE_CRTDEFS_H
119122

0 commit comments

Comments
 (0)