Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mVkAv-0005W2-SU for pgsql-hackers@arkaria.postgresql.org; Thu, 30 Sep 2021 00:43:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mVkAu-0001l7-Qq for pgsql-hackers@arkaria.postgresql.org; Thu, 30 Sep 2021 00:43:48 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mVkAu-0001kz-D1 for pgsql-hackers@lists.postgresql.org; Thu, 30 Sep 2021 00:43:48 +0000 Received: from mail-io1-xd2d.google.com ([2607:f8b0:4864:20::d2d]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1mVkAq-0008FH-Uq for pgsql-hackers@postgresql.org; Thu, 30 Sep 2021 00:43:47 +0000 Received: by mail-io1-xd2d.google.com with SMTP id p80so5409748iod.10 for ; Wed, 29 Sep 2021 17:43:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telsasoft-com.20210112.gappssmtp.com; s=20210112; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=Yj8knIcm7tntj5OirXUD5Vb3hlMr66CyuPugKsnPxrs=; b=BLANGlZI9R07wgh1nC/GIQdufr6GdDSI5bmzajh0DpUVMLnJ6JxoY7eAzZov/WxST7 slCZM7OpMLxpu1Qi+WUHbcxRwWGQ5/lnQB3hnKrBWj3a5Zau/nch/mOINyCOiLR0eYrv WsgzQZiqz7czjaM0TeX9KEhY4HVubGS8I2yt2vza2Z80CUefTNVZvCGILnpLg5N9oUje Z8SHORVTzk7HQwvEzPLmfLr1WDLx6xQrqEZf0DJr+o5aA6l5dUI+Vh5TFNnwGNGEcTLe kynxpd7XLYGbN6bPYtZyzs6jB6lRAxS+c7KmrlqhWN0Pz3UsDqWCUjITmIKGhV45EddW x7Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=Yj8knIcm7tntj5OirXUD5Vb3hlMr66CyuPugKsnPxrs=; b=YiL0Hw7Ffjo+Q8uorWa9+81VCXuPx2Y4fCDsbBzZha9vYVv72VM8svbTba9wbmpKRD NDkbrhcaLq7dFqTpK/HL0f+lJed+lvE1L+6A8NfTSX9v2p4mQq1XuBg/NDJrHV4bFIBD S1NFZxCS8LKoEYQYlzp2WfnxiEZ3AUVHlSV//Y8Z80z6HPziGDPadSD4xARqYcDtkOf1 CrCAycyYqV6rqsP90+WAJNealrHiuta51r9rHevc9Szb4g662JAn0FJTCFWtwlwBobJg +Hepr35g+J/eSyqj0x9L2Y23cQ7UWBiaGCIxHHILvHqMmiQxLJw7pOemqcbhF/PO/Lnv c+WQ== X-Gm-Message-State: AOAM531BFPidG8clq15/Pvwu1cb9CFt7Fx9CfggLGyEcNDaiDbtgT6zZ vsDQ4cBqKVUXuWjp49m+Q4lPlw== X-Google-Smtp-Source: ABdhPJzu1jGlw/PGNkUegOYlnshpuVOCmjwYWbFGCBH8JNpLhAvQx1XiW7bsdFUKD5OFFcd8SwbFAw== X-Received: by 2002:a05:6602:3149:: with SMTP id m9mr1803628ioy.81.1632962623452; Wed, 29 Sep 2021 17:43:43 -0700 (PDT) Received: from pryzbyj.telsasoft (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id h12sm899788ilj.81.2021.09.29.17.43.42 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 29 Sep 2021 17:43:42 -0700 (PDT) Received: by pryzbyj.telsasoft (Postfix, from userid 1000) id 44937800BC8; Wed, 29 Sep 2021 19:43:41 -0500 (CDT) Date: Wed, 29 Sep 2021 19:43:41 -0500 From: Justin Pryzby To: Thomas Munro Cc: David Steele , Fujii Masao , Bruce Momjian , Paul Guo , Tom Lane , Michael Brown , pgsql-hackers , Michael Paquier Subject: should frontend tools use syncfs() ? Message-ID: <20210930004340.GM831@telsasoft.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="P8VDRTGkfyc6BSVj" Content-Disposition: inline User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --P8VDRTGkfyc6BSVj Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Forking this thread in which Thomas implemented syncfs for the startup process (61752afb2). https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BSG9jSW3ekwib0cSdC0yD-jReJ21X4bZAmqxoWTLTc2A%40mail.gmail.com Is there any reason that initdb/pg_basebackup/pg_checksums/pg_rewind shouldn't use syncfs() ? do_syncfs() is in src/backend/ so would need to be duplicated^Wimplemented in common. They can't use the GUC, so need to add an cmdline option or look at an environment variable. --P8VDRTGkfyc6BSVj Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0001-Allow-use-of-syncfs-in-frontend-tools.patch" From 747ec6011d438a655ee26105a315119b75036c10 Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Wed, 29 Sep 2021 14:22:32 -0500 Subject: [PATCH] Allow use of syncfs() in frontend tools Like initdb/basebackup/checksums/rewind. See also: 61752afb26404dfc99a535c7a53f7f04dc110263 --- src/backend/utils/misc/guc.c | 1 + src/bin/initdb/initdb.c | 10 +++- src/bin/pg_basebackup/pg_basebackup.c | 2 +- src/bin/pg_checksums/pg_checksums.c | 2 +- src/bin/pg_rewind/file_ops.c | 2 +- src/common/file_utils.c | 72 ++++++++++++++++++++++++++- src/include/common/file_utils.h | 2 +- 7 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index d2ce4a8450..e37f6941f4 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -50,6 +50,7 @@ #include "commands/user.h" #include "commands/vacuum.h" #include "commands/variable.h" +#include "common/file_utils.h" #include "common/string.h" #include "funcapi.h" #include "jit/jit.h" diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 1ed4808d53..79048f3949 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -142,6 +142,7 @@ static bool noclean = false; static bool noinstructions = false; static bool do_sync = true; static bool sync_only = false; +static bool use_syncfs = false; static bool show_setting = false; static bool data_checksums = false; static char *xlog_dir = NULL; @@ -2200,6 +2201,7 @@ usage(const char *progname) printf(_(" --no-instructions do not print instructions for next steps\n")); printf(_(" -s, --show show internal settings\n")); printf(_(" -S, --sync-only only sync database files to disk, then exit\n")); + printf(_(" , --syncfs use syncfs() rather than recursive fsync()\n")); printf(_("\nOther options:\n")); printf(_(" -V, --version output version information, then exit\n")); printf(_(" -?, --help show this help, then exit\n")); @@ -2869,6 +2871,7 @@ main(int argc, char *argv[]) {"no-sync", no_argument, NULL, 'N'}, {"no-instructions", no_argument, NULL, 13}, {"sync-only", no_argument, NULL, 'S'}, + {"syncfs", no_argument, NULL, 15}, {"waldir", required_argument, NULL, 'X'}, {"wal-segsize", required_argument, NULL, 12}, {"data-checksums", no_argument, NULL, 'k'}, @@ -3020,6 +3023,9 @@ main(int argc, char *argv[]) extra_options, "-c debug_discard_caches=1"); break; + case 15: + use_syncfs = true; + break; default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), @@ -3064,7 +3070,7 @@ main(int argc, char *argv[]) fputs(_("syncing data to disk ... "), stdout); fflush(stdout); - fsync_pgdata(pg_data, PG_VERSION_NUM); + fsync_pgdata(pg_data, PG_VERSION_NUM, use_syncfs); check_ok(); return 0; } @@ -3153,7 +3159,7 @@ main(int argc, char *argv[]) { fputs(_("syncing data to disk ... "), stdout); fflush(stdout); - fsync_pgdata(pg_data, PG_VERSION_NUM); + fsync_pgdata(pg_data, PG_VERSION_NUM, use_syncfs); check_ok(); } else diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 669aa207a3..0b59b1fe32 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -2192,7 +2192,7 @@ BaseBackup(void) } else { - (void) fsync_pgdata(basedir, serverVersion); + (void) fsync_pgdata(basedir, serverVersion, false); } } diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c index e833c5d75e..021a4b18f9 100644 --- a/src/bin/pg_checksums/pg_checksums.c +++ b/src/bin/pg_checksums/pg_checksums.c @@ -687,7 +687,7 @@ main(int argc, char *argv[]) if (do_sync) { pg_log_info("syncing data directory"); - fsync_pgdata(DataDir, PG_VERSION_NUM); + fsync_pgdata(DataDir, PG_VERSION_NUM, false); } pg_log_info("updating control file"); diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c index c50f283ede..c50d5945ab 100644 --- a/src/bin/pg_rewind/file_ops.c +++ b/src/bin/pg_rewind/file_ops.c @@ -296,7 +296,7 @@ sync_target_dir(void) if (!do_sync || dry_run) return; - fsync_pgdata(datadir_target, PG_VERSION_NUM); + fsync_pgdata(datadir_target, PG_VERSION_NUM, false); } diff --git a/src/common/file_utils.c b/src/common/file_utils.c index 40b73bbe1a..c8b62e8aed 100644 --- a/src/common/file_utils.c +++ b/src/common/file_utils.c @@ -50,8 +50,33 @@ static void walkdir(const char *path, int (*action) (const char *fname, bool isdir), bool process_symlinks); +/* This is a frontend version of what's in src/backend/storage/file/fd.c */ +#ifdef HAVE_SYNCFS +static void +do_syncfs(const char *path) +{ + int fd; + + fd = open(path, O_RDONLY, 0); + if (fd < 0) + { + pg_log_error("could not open directory \"%s\": %m", path); + return; + } + if (syncfs(fd) < 0) + { + pg_log_fatal("could not synchronize file system for file \"%s\": %m", path); + exit(EXIT_FAILURE); + } + close(fd); +} +#endif + /* - * Issue fsync recursively on PGDATA and all its contents. + * Issue fsync recursively on PGDATA and all its contents, or issue syncfs for + * all potential filesystem, depending on recovery_init_sync_method setting. + * + * This is a frontend version of SyncDataDirectory. * * We fsync regular files and directories wherever they are, but we follow * symlinks only for pg_wal (or pg_xlog) and immediately under pg_tblspc. @@ -62,7 +87,8 @@ static void walkdir(const char *path, */ void fsync_pgdata(const char *pg_data, - int serverVersion) + int serverVersion, + bool use_syncfs) { bool xlog_is_symlink; char pg_wal[MAXPGPATH]; @@ -93,6 +119,48 @@ fsync_pgdata(const char *pg_data, xlog_is_symlink = true; #endif +#ifdef HAVE_SYNCFS + if (use_syncfs) + { + DIR *dir; + struct dirent *de; + + /* + * On Linux, we don't have to open every single file one by one. We + * can use syncfs() to sync whole filesystems. We only expect + * filesystem boundaries to exist where we tolerate symlinks, namely + * pg_wal and the tablespaces, so we call syncfs() for each of those + * directories. + */ + + /* Sync the top level pgdata directory. */ + do_syncfs(pg_data); + /* If any tablespaces are configured, sync each of those. */ + dir = opendir(pg_tblspc); + if (dir == NULL) + { + /* It's not a fatal error if pg_tblspc itself doesn't exist */ + pg_log_error("could not open directory \"%s\": %m", pg_tblspc); + } else { + while ((de = readdir(dir))) + { + char path[MAXPGPATH]; + + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + + snprintf(path, MAXPGPATH, "%s/%s", pg_tblspc, de->d_name); + do_syncfs(path); + } + closedir(dir); + } + /* If pg_wal is a symlink, process that too. */ + if (xlog_is_symlink) + do_syncfs(pg_wal); + return; + } +#endif /* !HAVE_SYNCFS */ + /* * If possible, hint to the kernel that we're soon going to fsync the data * directory and its contents. diff --git a/src/include/common/file_utils.h b/src/include/common/file_utils.h index 978a57460a..e622829c0b 100644 --- a/src/include/common/file_utils.h +++ b/src/include/common/file_utils.h @@ -26,7 +26,7 @@ typedef enum PGFileType #ifdef FRONTEND extern int fsync_fname(const char *fname, bool isdir); -extern void fsync_pgdata(const char *pg_data, int serverVersion); +extern void fsync_pgdata(const char *pg_data, int serverVersion, bool use_syncfs); extern void fsync_dir_recurse(const char *dir); extern int durable_rename(const char *oldfile, const char *newfile); extern int fsync_parent_path(const char *fname); -- 2.17.0 --P8VDRTGkfyc6BSVj--