1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-23 03:21:12 +03:00

initdb: Add --no-sync-data-files.

This new option instructs initdb to skip synchronizing any files
in database directories, the database directories themselves, and
the tablespace directories, i.e., everything in the base/
subdirectory and any other tablespace directories.  Other files,
such as those in pg_wal/ and pg_xact/, will still be synchronized
unless --no-sync is also specified.  --no-sync-data-files is
primarily intended for internal use by tools that separately ensure
the skipped files are synchronized to disk.  A follow-up commit
will use this to help optimize pg_upgrade's file transfer step.

The --sync-method=fsync implementation of this option makes use of
a new exclude_dir parameter for walkdir().  When not NULL,
exclude_dir specifies a directory to skip processing.  The
--sync-method=syncfs implementation of this option just skips
synchronizing the non-default tablespace directories.  This means
that initdb will still synchronize some or all of the database
files, but there's not much we can do about that.

Discussion: https://postgr.es/m/Zyvop-LxLXBLrZil%40nathan
This commit is contained in:
Nathan Bossart
2025-03-25 16:02:35 -05:00
parent 650ab8aaf1
commit cf131fa942
9 changed files with 99 additions and 40 deletions

View File

@ -527,6 +527,33 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="app-initdb-option-no-sync-data-files">
<term><option>--no-sync-data-files</option></term>
<listitem>
<para>
By default, <command>initdb</command> safely writes all database files
to disk. This option instructs <command>initdb</command> to skip
synchronizing all files in the individual database directories, the
database directories themselves, and the tablespace directories, i.e.,
everything in the <filename>base</filename> subdirectory and any other
tablespace directories. Other files, such as those in
<literal>pg_wal</literal> and <literal>pg_xact</literal>, will still be
synchronized unless the <option>--no-sync</option> option is also
specified.
</para>
<para>
Note that if <option>--no-sync-data-files</option> is used in
conjuction with <option>--sync-method=syncfs</option>, some or all of
the aforementioned files and directories will be synchronized because
<literal>syncfs</literal> processes entire file systems.
</para>
<para>
This option is primarily intended for internal use by tools that
separately ensure the skipped files are synchronized to disk.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-initdb-option-no-instructions"> <varlistentry id="app-initdb-option-no-instructions">
<term><option>--no-instructions</option></term> <term><option>--no-instructions</option></term>
<listitem> <listitem>

View File

@ -168,6 +168,7 @@ static bool data_checksums = true;
static char *xlog_dir = NULL; static char *xlog_dir = NULL;
static int wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024); static int wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024);
static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC; static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
static bool sync_data_files = true;
/* internal vars */ /* internal vars */
@ -2566,6 +2567,7 @@ usage(const char *progname)
printf(_(" -L DIRECTORY where to find the input files\n")); printf(_(" -L DIRECTORY where to find the input files\n"));
printf(_(" -n, --no-clean do not clean up after errors\n")); printf(_(" -n, --no-clean do not clean up after errors\n"));
printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n")); printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-sync-data-files do not sync files within database directories\n"));
printf(_(" --no-instructions do not print instructions for next steps\n")); printf(_(" --no-instructions do not print instructions for next steps\n"));
printf(_(" -s, --show show internal settings, then exit\n")); printf(_(" -s, --show show internal settings, then exit\n"));
printf(_(" --sync-method=METHOD set method for syncing files to disk\n")); printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
@ -3208,6 +3210,7 @@ main(int argc, char *argv[])
{"icu-rules", required_argument, NULL, 18}, {"icu-rules", required_argument, NULL, 18},
{"sync-method", required_argument, NULL, 19}, {"sync-method", required_argument, NULL, 19},
{"no-data-checksums", no_argument, NULL, 20}, {"no-data-checksums", no_argument, NULL, 20},
{"no-sync-data-files", no_argument, NULL, 21},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
@ -3402,6 +3405,9 @@ main(int argc, char *argv[])
case 20: case 20:
data_checksums = false; data_checksums = false;
break; break;
case 21:
sync_data_files = false;
break;
default: default:
/* getopt_long already emitted a complaint */ /* getopt_long already emitted a complaint */
pg_log_error_hint("Try \"%s --help\" for more information.", progname); pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@ -3453,7 +3459,7 @@ main(int argc, char *argv[])
fputs(_("syncing data to disk ... "), stdout); fputs(_("syncing data to disk ... "), stdout);
fflush(stdout); fflush(stdout);
sync_pgdata(pg_data, PG_VERSION_NUM, sync_method); sync_pgdata(pg_data, PG_VERSION_NUM, sync_method, sync_data_files);
check_ok(); check_ok();
return 0; return 0;
} }
@ -3516,7 +3522,7 @@ main(int argc, char *argv[])
{ {
fputs(_("syncing data to disk ... "), stdout); fputs(_("syncing data to disk ... "), stdout);
fflush(stdout); fflush(stdout);
sync_pgdata(pg_data, PG_VERSION_NUM, sync_method); sync_pgdata(pg_data, PG_VERSION_NUM, sync_method, sync_data_files);
check_ok(); check_ok();
} }
else else

View File

@ -76,6 +76,7 @@ command_like(
'checksums are enabled in control file'); 'checksums are enabled in control file');
command_ok([ 'initdb', '--sync-only', $datadir ], 'sync only'); command_ok([ 'initdb', '--sync-only', $datadir ], 'sync only');
command_ok([ 'initdb', '--sync-only', '--no-sync-data-files', $datadir ], '--no-sync-data-files');
command_fails([ 'initdb', $datadir ], 'existing data directory'); command_fails([ 'initdb', $datadir ], 'existing data directory');
if ($supports_syncfs) if ($supports_syncfs)

View File

@ -2310,7 +2310,7 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
} }
else else
{ {
(void) sync_pgdata(basedir, serverVersion, sync_method); (void) sync_pgdata(basedir, serverVersion, sync_method, true);
} }
} }

View File

@ -633,7 +633,7 @@ main(int argc, char *argv[])
if (do_sync) if (do_sync)
{ {
pg_log_info("syncing data directory"); pg_log_info("syncing data directory");
sync_pgdata(DataDir, PG_VERSION_NUM, sync_method); sync_pgdata(DataDir, PG_VERSION_NUM, sync_method, true);
} }
pg_log_info("updating control file"); pg_log_info("updating control file");

View File

@ -424,7 +424,7 @@ main(int argc, char *argv[])
else else
{ {
pg_log_debug("recursively fsyncing \"%s\"", opt.output); pg_log_debug("recursively fsyncing \"%s\"", opt.output);
sync_pgdata(opt.output, version * 10000, opt.sync_method); sync_pgdata(opt.output, version * 10000, opt.sync_method, true);
} }
} }

View File

@ -296,7 +296,7 @@ sync_target_dir(void)
if (!do_sync || dry_run) if (!do_sync || dry_run)
return; return;
sync_pgdata(datadir_target, PG_VERSION_NUM, sync_method); sync_pgdata(datadir_target, PG_VERSION_NUM, sync_method, true);
} }

View File

@ -50,7 +50,8 @@ static int pre_sync_fname(const char *fname, bool isdir);
#endif #endif
static void walkdir(const char *path, static void walkdir(const char *path,
int (*action) (const char *fname, bool isdir), int (*action) (const char *fname, bool isdir),
bool process_symlinks); bool process_symlinks,
const char *exclude_dir);
#ifdef HAVE_SYNCFS #ifdef HAVE_SYNCFS
@ -93,11 +94,15 @@ do_syncfs(const char *path)
* syncing, and might not have privileges to write at all. * syncing, and might not have privileges to write at all.
* *
* serverVersion indicates the version of the server to be sync'd. * serverVersion indicates the version of the server to be sync'd.
*
* If sync_data_files is false, this function skips syncing "base/" and any
* other tablespace directories.
*/ */
void void
sync_pgdata(const char *pg_data, sync_pgdata(const char *pg_data,
int serverVersion, int serverVersion,
DataDirSyncMethod sync_method) DataDirSyncMethod sync_method,
bool sync_data_files)
{ {
bool xlog_is_symlink; bool xlog_is_symlink;
char pg_wal[MAXPGPATH]; char pg_wal[MAXPGPATH];
@ -147,6 +152,8 @@ sync_pgdata(const char *pg_data,
do_syncfs(pg_data); do_syncfs(pg_data);
/* If any tablespaces are configured, sync each of those. */ /* If any tablespaces are configured, sync each of those. */
if (sync_data_files)
{
dir = opendir(pg_tblspc); dir = opendir(pg_tblspc);
if (dir == NULL) if (dir == NULL)
pg_log_error("could not open directory \"%s\": %m", pg_log_error("could not open directory \"%s\": %m",
@ -172,6 +179,7 @@ sync_pgdata(const char *pg_data,
(void) closedir(dir); (void) closedir(dir);
} }
}
/* If pg_wal is a symlink, process that too. */ /* If pg_wal is a symlink, process that too. */
if (xlog_is_symlink) if (xlog_is_symlink)
@ -182,15 +190,21 @@ sync_pgdata(const char *pg_data,
case DATA_DIR_SYNC_METHOD_FSYNC: case DATA_DIR_SYNC_METHOD_FSYNC:
{ {
char *exclude_dir = NULL;
if (!sync_data_files)
exclude_dir = psprintf("%s/base", pg_data);
/* /*
* If possible, hint to the kernel that we're soon going to * If possible, hint to the kernel that we're soon going to
* fsync the data directory and its contents. * fsync the data directory and its contents.
*/ */
#ifdef PG_FLUSH_DATA_WORKS #ifdef PG_FLUSH_DATA_WORKS
walkdir(pg_data, pre_sync_fname, false); walkdir(pg_data, pre_sync_fname, false, exclude_dir);
if (xlog_is_symlink) if (xlog_is_symlink)
walkdir(pg_wal, pre_sync_fname, false); walkdir(pg_wal, pre_sync_fname, false, NULL);
walkdir(pg_tblspc, pre_sync_fname, true); if (sync_data_files)
walkdir(pg_tblspc, pre_sync_fname, true, NULL);
#endif #endif
/* /*
@ -203,10 +217,14 @@ sync_pgdata(const char *pg_data,
* get fsync'd twice. That's not an expected case so we don't * get fsync'd twice. That's not an expected case so we don't
* worry about optimizing it. * worry about optimizing it.
*/ */
walkdir(pg_data, fsync_fname, false); walkdir(pg_data, fsync_fname, false, exclude_dir);
if (xlog_is_symlink) if (xlog_is_symlink)
walkdir(pg_wal, fsync_fname, false); walkdir(pg_wal, fsync_fname, false, NULL);
walkdir(pg_tblspc, fsync_fname, true); if (sync_data_files)
walkdir(pg_tblspc, fsync_fname, true, NULL);
if (exclude_dir)
pfree(exclude_dir);
} }
break; break;
} }
@ -245,10 +263,10 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
* fsync the data directory and its contents. * fsync the data directory and its contents.
*/ */
#ifdef PG_FLUSH_DATA_WORKS #ifdef PG_FLUSH_DATA_WORKS
walkdir(dir, pre_sync_fname, false); walkdir(dir, pre_sync_fname, false, NULL);
#endif #endif
walkdir(dir, fsync_fname, false); walkdir(dir, fsync_fname, false, NULL);
} }
break; break;
} }
@ -264,6 +282,9 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
* ignored in subdirectories, ie we intentionally don't pass down the * ignored in subdirectories, ie we intentionally don't pass down the
* process_symlinks flag to recursive calls. * process_symlinks flag to recursive calls.
* *
* If exclude_dir is not NULL, it specifies a directory path to skip
* processing.
*
* Errors are reported but not considered fatal. * Errors are reported but not considered fatal.
* *
* See also walkdir in fd.c, which is a backend version of this logic. * See also walkdir in fd.c, which is a backend version of this logic.
@ -271,11 +292,15 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
static void static void
walkdir(const char *path, walkdir(const char *path,
int (*action) (const char *fname, bool isdir), int (*action) (const char *fname, bool isdir),
bool process_symlinks) bool process_symlinks,
const char *exclude_dir)
{ {
DIR *dir; DIR *dir;
struct dirent *de; struct dirent *de;
if (exclude_dir && strcmp(exclude_dir, path) == 0)
return;
dir = opendir(path); dir = opendir(path);
if (dir == NULL) if (dir == NULL)
{ {
@ -299,7 +324,7 @@ walkdir(const char *path,
(*action) (subpath, false); (*action) (subpath, false);
break; break;
case PGFILETYPE_DIR: case PGFILETYPE_DIR:
walkdir(subpath, action, false); walkdir(subpath, action, false, exclude_dir);
break; break;
default: default:

View File

@ -35,7 +35,7 @@ struct iovec; /* avoid including port/pg_iovec.h here */
#ifdef FRONTEND #ifdef FRONTEND
extern int fsync_fname(const char *fname, bool isdir); extern int fsync_fname(const char *fname, bool isdir);
extern void sync_pgdata(const char *pg_data, int serverVersion, extern void sync_pgdata(const char *pg_data, int serverVersion,
DataDirSyncMethod sync_method); DataDirSyncMethod sync_method, bool sync_data_files);
extern void sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method); extern void sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method);
extern int durable_rename(const char *oldfile, const char *newfile); extern int durable_rename(const char *oldfile, const char *newfile);
extern int fsync_parent_path(const char *fname); extern int fsync_parent_path(const char *fname);