Allow using syncfs() in frontend utilities.
authorNathan Bossart
Wed, 6 Sep 2023 23:27:16 +0000 (16:27 -0700)
committerNathan Bossart
Wed, 6 Sep 2023 23:27:16 +0000 (16:27 -0700)
This commit allows specifying a --sync-method in several frontend
utilities that must synchronize many files to disk (initdb,
pg_basebackup, pg_checksums, pg_dump, pg_rewind, and pg_upgrade).
On Linux, users can specify "syncfs" to synchronize the relevant
file systems instead of calling fsync() for every single file.  In
many cases, using syncfs() is much faster.

As with recovery_init_sync_method, this new option comes with some
caveats.  The descriptions of these caveats have been moved to a
new appendix section in the documentation.

Co-authored-by: Justin Pryzby
Reviewed-by: Michael Paquier, Thomas Munro, Robert Haas, Justin Pryzby
Discussion: https://postgr.es/m/20210930004340.GM831%40telsasoft.com

21 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/filelist.sgml
doc/src/sgml/postgres.sgml
doc/src/sgml/ref/initdb.sgml
doc/src/sgml/ref/pg_basebackup.sgml
doc/src/sgml/ref/pg_checksums.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_rewind.sgml
doc/src/sgml/ref/pgupgrade.sgml
doc/src/sgml/syncfs.sgml [new file with mode: 0644]
src/bin/initdb/initdb.c
src/bin/initdb/t/001_initdb.pl
src/bin/pg_basebackup/pg_basebackup.c
src/bin/pg_checksums/pg_checksums.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_rewind/pg_rewind.c
src/bin/pg_upgrade/option.c
src/bin/pg_upgrade/pg_upgrade.c
src/bin/pg_upgrade/pg_upgrade.h
src/fe_utils/option_utils.c
src/include/fe_utils/option_utils.h

index f0a50a5f9ad0dd829e8f4406d0b15549777e2c2f..6bc1b215db99dc53a16403061cccf46cb63549f1 100644 (file)
@@ -10511,15 +10511,9 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
         On Linux, syncfs may be used instead, to ask the
         operating system to synchronize the whole file systems that contain the
         data directory, the WAL files and each tablespace (but not any other
-        file systems that may be reachable through symbolic links).  This may
-        be a lot faster than the fsync setting, because it
-        doesn't need to open each file one by one.  On the other hand, it may
-        be slower if a file system is shared by other applications that
-        modify a lot of files, since those files will also be written to disk.
-        Furthermore, on versions of Linux before 5.8, I/O errors encountered
-        while writing data to disk may not be reported to
-        PostgreSQL, and relevant error messages may
-        appear only in kernel logs.
+        file systems that may be reachable through symbolic links).  See
+         for more information about using
+        syncfs().
        
        
         This parameter can only be set in the
index e3d94a62b3f5a2668e58e1d38c7a43e75cbbc685..4c63a7e76895b5291af18d14b9c3116d7cc63878 100644 (file)
 
 
 
+
 
 
 
index 2e271862fc1800086a64a94b2173ea21142a1628..f629524be07fcd978b2d9eab8b30d6ca4cf14a52 100644 (file)
@@ -294,6 +294,7 @@ break is not needed in a wider output rendering.
   &acronyms;
   &glossary;
   &color;
+  &syncfs;
   &obsolete;
 
  
index 22f1011781fb8ce2ec04ea46a6f66fb22abd89d3..8a09c5c4388322823d1f33d91ce6853535669a7d 100644 (file)
@@ -365,6 +365,28 @@ PostgreSQL documentation
       
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        initdb will recursively open and synchronize all
+        files in the data directory.  The search for files will follow symbolic
+        links for the WAL directory and each configured tablespace.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file systems that contain the
+        data directory, the WAL files, and each tablespace.  See
+         for more information about using
+        syncfs().
+       
+       
+        This option has no effect when  is used.
+       
+      
+     
+
      
       
       
index 79d3e657c32c245f9825c602d2760c0c3f49d9e1..d2b8ddd200c271bf86adb3adaab2f80987ab1987 100644 (file)
@@ -594,6 +594,31 @@ PostgreSQL documentation
       
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        pg_basebackup will recursively open and synchronize
+        all files in the backup directory.  When the plain format is used, the
+        search for files will follow symbolic links for the WAL directory and
+        each configured tablespace.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file system that contains the
+        backup directory.  When the plain format is used,
+        pg_basebackup will also synchronize the file systems
+        that contain the WAL files and each tablespace.  See
+         for more information about using
+        syncfs().
+       
+       
+        This option has no effect when  is used.
+       
+      
+     
+
      
       
       
index a3d0b0f0a3d64e26792e89dbed52c16642c19774..7b44ba71cf97210b19efd9945d9098a0952f9ae3 100644 (file)
@@ -139,6 +139,28 @@ PostgreSQL documentation
       
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        pg_checksums will recursively open and synchronize
+        all files in the data directory.  The search for files will follow
+        symbolic links for the WAL directory and each configured tablespace.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file systems that contain the
+        data directory, the WAL files, and each tablespace.  See
+         for more information about using
+        syncfs().
+       
+       
+        This option has no effect when  is used.
+       
+      
+     
+
      
       
       
index a3cf0608f5b1475814e750d366d56fe2ab00327a..c1e2220b3cbfbff087f187c4bc987dcffd5424a9 100644 (file)
@@ -1179,6 +1179,27 @@ PostgreSQL documentation
       
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        pg_dump --format=directory will recursively open and
+        synchronize all files in the archive directory.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file system that contains the
+        archive directory.  See  for more information
+        about using syncfs().
+       
+       
+        This option has no effect when  is used or
+         is not set to directory.
+       
+      
+     
+
      
       
       
index 15cddd086b756b97f6d337622f118ecfeb050aa2..80dff1616820864123b29d47883d48276b68b077 100644 (file)
@@ -284,6 +284,28 @@ PostgreSQL documentation
       
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        pg_rewind will recursively open and synchronize all
+        files in the data directory.  The search for files will follow symbolic
+        links for the WAL directory and each configured tablespace.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file systems that contain the
+        data directory, the WAL files, and each tablespace.  See
+         for more information about using
+        syncfs().
+       
+       
+        This option has no effect when  is used.
+       
+      
+     
+
      
       
       
index 7816b4c6859bfa1c9df705f6a32cb538336d8739..bea0d1b93f930ee074dbf3f094e656fdb58e8eca 100644 (file)
@@ -190,6 +190,29 @@ PostgreSQL documentation
       variable PGSOCKETDIR
      
 
+     
+      
+      
+       
+        When set to fsync, which is the default,
+        pg_upgrade will recursively open and synchronize all
+        files in the upgraded cluster's data directory.  The search for files
+        will follow symbolic links for the WAL directory and each configured
+        tablespace.
+       
+       
+        On Linux, syncfs may be used instead to ask the
+        operating system to synchronize the whole file systems that contain the
+        upgraded cluster's data directory, its WAL files, and each tablespace.
+        See  for more information about using
+        syncfs().
+       
+       
+        This option has no effect when  is used.
+       
+      
+     
+
      
        username
       username
diff --git a/doc/src/sgml/syncfs.sgml b/doc/src/sgml/syncfs.sgml
new file mode 100644 (file)
index 0000000..00457d2
--- /dev/null
@@ -0,0 +1,36 @@
+
+
+
<function>syncfs()</function> Caveats
+
+  syncfs
+
+  On Linux syncfs() may be specified for some
+  configuration parameters (e.g.,
+  ), server applications (e.g.,
+  pg_upgrade), and client applications (e.g.,
+  pg_basebackup) that involve synchronizing many
+  files to disk.  syncfs() is advantageous in many cases,
+  but there are some trade-offs to keep in mind.
+
+  Since syncfs() instructs the operating system to
+  synchronize a whole file system, it typically requires many fewer system
+  calls than using fsync() to synchronize each file one by
+  one.  Therefore, using syncfs() may be a lot faster than
+  using fsync().  However, it may be slower if a file
+  system is shared by other applications that modify a lot of files, since
+  those files will also be written to disk.
+
+  Furthermore, on versions of Linux before 5.8, I/O errors encountered while
+  writing data to disk may not be reported to the calling program, and relevant
+  error messages may appear only in kernel logs.
+
+
index 51198e666554b3a18777e714fa293b45b8309e6d..bddb30d766cb2170a70d3a807e9568a20122bd48 100644 (file)
@@ -2467,6 +2467,7 @@ usage(const char *progname)
    printf(_("  -N, --no-sync             do not wait for changes to be written safely to disk\n"));
    printf(_("      --no-instructions     do not print instructions for next steps\n"));
    printf(_("  -s, --show                show internal settings\n"));
+   printf(_("      --sync-method=METHOD  set method for syncing files to disk\n"));
    printf(_("  -S, --sync-only           only sync database files to disk, then exit\n"));
    printf(_("\nOther options:\n"));
    printf(_("  -V, --version             output version information, then exit\n"));
@@ -3107,6 +3108,7 @@ main(int argc, char *argv[])
        {"locale-provider", required_argument, NULL, 15},
        {"icu-locale", required_argument, NULL, 16},
        {"icu-rules", required_argument, NULL, 17},
+       {"sync-method", required_argument, NULL, 18},
        {NULL, 0, NULL, 0}
    };
 
@@ -3287,6 +3289,10 @@ main(int argc, char *argv[])
            case 17:
                icu_rules = pg_strdup(optarg);
                break;
+           case 18:
+               if (!parse_sync_method(optarg, &sync_method))
+                   exit(1);
+               break;
            default:
                /* getopt_long already emitted a complaint */
                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
index 2d7469d2fc339a3b045be69a1012e3e74ed038da..45f96cd8bbf04991d54555c6a80c7d6e1bfe1f7c 100644 (file)
@@ -16,6 +16,7 @@ use Test::More;
 my $tempdir = PostgreSQL::Test::Utils::tempdir;
 my $xlogdir = "$tempdir/pgxlog";
 my $datadir = "$tempdir/data";
+my $supports_syncfs = check_pg_config("#define HAVE_SYNCFS 1");
 
 program_help_ok('initdb');
 program_version_ok('initdb');
@@ -82,6 +83,17 @@ command_fails([ 'pg_checksums', '-D', $datadir ],
 command_ok([ 'initdb', '-S', $datadir ], 'sync only');
 command_fails([ 'initdb', $datadir ], 'existing data directory');
 
+if ($supports_syncfs)
+{
+   command_ok([ 'initdb', '-S', $datadir, '--sync-method', 'syncfs' ],
+       'sync method syncfs');
+}
+else
+{
+   command_fails([ 'initdb', '-S', $datadir, '--sync-method', 'syncfs' ],
+       'sync method syncfs');
+}
+
 # Check group access on PGDATA
 SKIP:
 {
index e9033af5c031fab6166d5d0d992ae8af19b50480..74f5332e95652a3a720db8699ce2e0c4bfda7107 100644 (file)
@@ -425,6 +425,8 @@ usage(void)
    printf(_("      --no-slot          prevent creation of temporary replication slot\n"));
    printf(_("      --no-verify-checksums\n"
             "                         do not verify checksums\n"));
+   printf(_("      --sync-method=METHOD\n"
+            "                         set method for syncing files to disk\n"));
    printf(_("  -?, --help             show this help, then exit\n"));
    printf(_("\nConnection options:\n"));
    printf(_("  -d, --dbname=CONNSTR   connection string\n"));
@@ -2282,6 +2284,7 @@ main(int argc, char **argv)
        {"no-manifest", no_argument, NULL, 5},
        {"manifest-force-encode", no_argument, NULL, 6},
        {"manifest-checksums", required_argument, NULL, 7},
+       {"sync-method", required_argument, NULL, 8},
        {NULL, 0, NULL, 0}
    };
    int         c;
@@ -2453,6 +2456,10 @@ main(int argc, char **argv)
            case 7:
                manifest_checksums = pg_strdup(optarg);
                break;
+           case 8:
+               if (!parse_sync_method(optarg, &sync_method))
+                   exit(1);
+               break;
            default:
                /* getopt_long already emitted a complaint */
                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
index 836ee65405904b7673f48b50e243470cc990c41b..e009ba5e0bc9509ace3e696b227d0d729d28fc2f 100644 (file)
@@ -78,6 +78,7 @@ usage(void)
    printf(_("  -f, --filenode=FILENODE  check only relation with specified filenode\n"));
    printf(_("  -N, --no-sync            do not wait for changes to be written safely to disk\n"));
    printf(_("  -P, --progress           show progress information\n"));
+   printf(_("      --sync-method=METHOD set method for syncing files to disk\n"));
    printf(_("  -v, --verbose            output verbose messages\n"));
    printf(_("  -V, --version            output version information, then exit\n"));
    printf(_("  -?, --help               show this help, then exit\n"));
@@ -436,6 +437,7 @@ main(int argc, char *argv[])
        {"no-sync", no_argument, NULL, 'N'},
        {"progress", no_argument, NULL, 'P'},
        {"verbose", no_argument, NULL, 'v'},
+       {"sync-method", required_argument, NULL, 1},
        {NULL, 0, NULL, 0}
    };
 
@@ -494,6 +496,10 @@ main(int argc, char *argv[])
            case 'v':
                verbose = true;
                break;
+           case 1:
+               if (!parse_sync_method(optarg, &sync_method))
+                   exit(1);
+               break;
            default:
                /* getopt_long already emitted a complaint */
                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
index 280662bc3321dc29e54e7c6b7032597683c9c907..f7b61766921370cc212f776ce1de5036da4bbad7 100644 (file)
@@ -432,6 +432,7 @@ main(int argc, char **argv)
        {"table-and-children", required_argument, NULL, 12},
        {"exclude-table-and-children", required_argument, NULL, 13},
        {"exclude-table-data-and-children", required_argument, NULL, 14},
+       {"sync-method", required_argument, NULL, 15},
 
        {NULL, 0, NULL, 0}
    };
@@ -658,6 +659,11 @@ main(int argc, char **argv)
                                          optarg);
                break;
 
+           case 15:
+               if (!parse_sync_method(optarg, &sync_method))
+                   exit_nicely(1);
+               break;
+
            default:
                /* getopt_long already emitted a complaint */
                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -1069,6 +1075,7 @@ help(const char *progname)
             "                               compress as specified\n"));
    printf(_("  --lock-wait-timeout=TIMEOUT  fail after waiting TIMEOUT for a table lock\n"));
    printf(_("  --no-sync                    do not wait for changes to be written safely to disk\n"));
+   printf(_("  --sync-method=METHOD         set method for syncing files to disk\n"));
    printf(_("  -?, --help                   show this help, then exit\n"));
 
    printf(_("\nOptions controlling the output content:\n"));
index bdfacf32632c780bb6bf085585654f1182195574..bfd44a284e224080e41e50b942d2adb134cedd88 100644 (file)
@@ -22,6 +22,7 @@
 #include "common/file_perm.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/recovery_gen.h"
 #include "fe_utils/string_utils.h"
 #include "file_ops.h"
@@ -108,6 +109,7 @@ usage(const char *progname)
             "                                 file when running target cluster\n"));
    printf(_("      --debug                    write a lot of debug messages\n"));
    printf(_("      --no-ensure-shutdown       do not automatically fix unclean shutdown\n"));
+   printf(_("      --sync-method=METHOD       set method for syncing files to disk\n"));
    printf(_("  -V, --version                  output version information, then exit\n"));
    printf(_("  -?, --help                     show this help, then exit\n"));
    printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
@@ -132,6 +134,7 @@ main(int argc, char **argv)
        {"no-sync", no_argument, NULL, 'N'},
        {"progress", no_argument, NULL, 'P'},
        {"debug", no_argument, NULL, 3},
+       {"sync-method", required_argument, NULL, 6},
        {NULL, 0, NULL, 0}
    };
    int         option_index;
@@ -219,6 +222,11 @@ main(int argc, char **argv)
                config_file = pg_strdup(optarg);
                break;
 
+           case 6:
+               if (!parse_sync_method(optarg, &sync_method))
+                   exit(1);
+               break;
+
            default:
                /* getopt_long already emitted a complaint */
                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
index 640361009e30a5f4c9b7593ef8d93a53c2e395bf..b9d900d0db46096a9477a0156893f14cc1433f7f 100644 (file)
@@ -14,6 +14,7 @@
 #endif
 
 #include "common/string.h"
+#include "fe_utils/option_utils.h"
 #include "getopt_long.h"
 #include "pg_upgrade.h"
 #include "utils/pidfile.h"
@@ -57,12 +58,14 @@ parseCommandLine(int argc, char *argv[])
        {"verbose", no_argument, NULL, 'v'},
        {"clone", no_argument, NULL, 1},
        {"copy", no_argument, NULL, 2},
+       {"sync-method", required_argument, NULL, 3},
 
        {NULL, 0, NULL, 0}
    };
    int         option;         /* Command line option */
    int         optindex = 0;   /* used by getopt_long */
    int         os_user_effective_id;
+   DataDirSyncMethod unused;
 
    user_opts.do_sync = true;
    user_opts.transfer_mode = TRANSFER_MODE_COPY;
@@ -199,6 +202,12 @@ parseCommandLine(int argc, char *argv[])
                user_opts.transfer_mode = TRANSFER_MODE_COPY;
                break;
 
+           case 3:
+               if (!parse_sync_method(optarg, &unused))
+                   exit(1);
+               user_opts.sync_method = pg_strdup(optarg);
+               break;
+
            default:
                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
                        os_info.progname);
@@ -209,6 +218,9 @@ parseCommandLine(int argc, char *argv[])
    if (optind < argc)
        pg_fatal("too many command-line arguments (first is \"%s\")", argv[optind]);
 
+   if (!user_opts.sync_method)
+       user_opts.sync_method = pg_strdup("fsync");
+
    if (log_opts.verbose)
        pg_log(PG_REPORT, "Running in verbose mode");
 
@@ -289,6 +301,7 @@ usage(void)
    printf(_("  -V, --version                 display version information, then exit\n"));
    printf(_("  --clone                       clone instead of copying files to new cluster\n"));
    printf(_("  --copy                        copy files to new cluster (default)\n"));
+   printf(_("  --sync-method=METHOD          set method for syncing files to disk\n"));
    printf(_("  -?, --help                    show this help, then exit\n"));
    printf(_("\n"
             "Before running pg_upgrade you must:\n"
index 4562dafcff561ca615e781775a1df090896ff8ef..96bfb67167f736b3b2ceef0f31e9da50a9b83328 100644 (file)
@@ -192,8 +192,10 @@ main(int argc, char **argv)
    {
        prep_status("Sync data directory to disk");
        exec_prog(UTILITY_LOG_FILE, NULL, true, true,
-                 "\"%s/initdb\" --sync-only \"%s\"", new_cluster.bindir,
-                 new_cluster.pgdata);
+                 "\"%s/initdb\" --sync-only \"%s\" --sync-method %s",
+                 new_cluster.bindir,
+                 new_cluster.pgdata,
+                 user_opts.sync_method);
        check_ok();
    }
 
index 7afa96716ec781cdedea77b2f4eb2600496991ac..842f3b6cd377ec163850ded0b1da1334ae553b22 100644 (file)
@@ -304,6 +304,7 @@ typedef struct
    transferMode transfer_mode; /* copy files or link them? */
    int         jobs;           /* number of processes/threads to use */
    char       *socketdir;      /* directory to use for Unix sockets */
+   char       *sync_method;
 } UserOpts;
 
 typedef struct
index 763c991015ba72da6bde721379fe2e7bedeac8f7..d2a3adeb4ba13784f4beb294d64be32e1b18b721 100644 (file)
@@ -82,3 +82,30 @@ option_parse_int(const char *optarg, const char *optname,
        *result = val;
    return true;
 }
+
+/*
+ * Provide strictly harmonized handling of the --sync-method option.
+ */
+bool
+parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
+{
+   if (strcmp(optarg, "fsync") == 0)
+       *sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
+   else if (strcmp(optarg, "syncfs") == 0)
+   {
+#ifdef HAVE_SYNCFS
+       *sync_method = DATA_DIR_SYNC_METHOD_SYNCFS;
+#else
+       pg_log_error("this build does not support sync method \"%s\"",
+                    "syncfs");
+       return false;
+#endif
+   }
+   else
+   {
+       pg_log_error("unrecognized sync method: %s", optarg);
+       return false;
+   }
+
+   return true;
+}
index b7b0654cee7a6030a67769a1cdcffe788bc3c7c9..6f3a965916a93fbc43f6c28a714b1b56326f06ce 100644 (file)
@@ -14,6 +14,8 @@
 
 #include "postgres_fe.h"
 
+#include "common/file_utils.h"
+
 typedef void (*help_handler) (const char *progname);
 
 extern void handle_help_version_opts(int argc, char *argv[],
@@ -22,5 +24,7 @@ extern void handle_help_version_opts(int argc, char *argv[],
 extern bool option_parse_int(const char *optarg, const char *optname,
                             int min_range, int max_range,
                             int *result);
+extern bool parse_sync_method(const char *optarg,
+                             DataDirSyncMethod *sync_method);
 
 #endif                         /* OPTION_UTILS_H */