Refactor more code logic to update the control file
authorMichael Paquier <[email protected]>
Mon, 18 Mar 2019 03:59:35 +0000 (12:59 +0900)
committerMichael Paquier <[email protected]>
Mon, 18 Mar 2019 03:59:35 +0000 (12:59 +0900)
ce6afc6 has begun the refactoring work by plugging pg_rewind into a
central routine to update the control file, and left around two extra
copies, with one in xlog.c for the backend and one in pg_resetwal.c.  By
adding an extra option to the central routine in controldata_utils.c to
control if a flush of the control file needs to be done, it is proving
to be straight-forward to make xlog.c and pg_resetwal.c use the central
code path at the condition of moving the wait event tracking there.
Hence, this allows to have only one central code path to update the
control file, shaving the code from the duplicates.

This refactoring actually fixes a problem in pg_resetwal.  Previously,
the control file was first removed before being recreated.  So if a
crash happened between the moment the file was removed and the moment
the file was created, then it would have been possible to not have a
control file anymore in the database folder.

Author: Fabien Coelho
Reviewed-by: Michael Paquier
Discussion: https://postgr.es/m/alpine.DEB.2.21.1903170935210.2506@lancre

src/backend/access/transam/xlog.c
src/bin/pg_resetwal/pg_resetwal.c
src/bin/pg_rewind/pg_rewind.c
src/common/controldata_utils.c
src/include/common/controldata_utils.h

index 54d3c558c64171782ece3b4fd20445a683b44a2e..ad12ebc4269e3f85f1a61f505503138bac7c2a83 100644 (file)
@@ -40,6 +40,7 @@
 #include "catalog/pg_control.h"
 #include "catalog/pg_database.h"
 #include "commands/tablespace.h"
+#include "common/controldata_utils.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -4754,51 +4755,14 @@ ReadControlFile(void)
                    PGC_INTERNAL, PGC_S_OVERRIDE);
 }
 
+/*
+ * Utility wrapper to update the control file.  Note that the control
+ * file gets flushed.
+ */
 void
 UpdateControlFile(void)
 {
-   int         fd;
-
-   INIT_CRC32C(ControlFile->crc);
-   COMP_CRC32C(ControlFile->crc,
-               (char *) ControlFile,
-               offsetof(ControlFileData, crc));
-   FIN_CRC32C(ControlFile->crc);
-
-   fd = BasicOpenFile(XLOG_CONTROL_FILE,
-                      O_RDWR | PG_BINARY);
-   if (fd < 0)
-       ereport(PANIC,
-               (errcode_for_file_access(),
-                errmsg("could not open file \"%s\": %m", XLOG_CONTROL_FILE)));
-
-   errno = 0;
-   pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE);
-   if (write(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
-   {
-       /* if write didn't set errno, assume problem is no disk space */
-       if (errno == 0)
-           errno = ENOSPC;
-       ereport(PANIC,
-               (errcode_for_file_access(),
-                errmsg("could not write to file \"%s\": %m",
-                       XLOG_CONTROL_FILE)));
-   }
-   pgstat_report_wait_end();
-
-   pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE);
-   if (pg_fsync(fd) != 0)
-       ereport(PANIC,
-               (errcode_for_file_access(),
-                errmsg("could not fsync file \"%s\": %m",
-                       XLOG_CONTROL_FILE)));
-   pgstat_report_wait_end();
-
-   if (close(fd))
-       ereport(PANIC,
-               (errcode_for_file_access(),
-                errmsg("could not close file \"%s\": %m",
-                       XLOG_CONTROL_FILE)));
+   update_controlfile(DataDir, NULL, ControlFile, true);
 }
 
 /*
index 2af8713216664a31345dfd387186ba540661a351..a7b25ffe1cd85f4edd04ff12d514f1f689abbd40 100644 (file)
@@ -49,8 +49,7 @@
 #include "access/multixact.h"
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
-#include "catalog/catversion.h"
-#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/restricted_token.h"
@@ -918,18 +917,6 @@ PrintNewControlValues(void)
 static void
 RewriteControlFile(void)
 {
-   int         fd;
-   char        buffer[PG_CONTROL_FILE_SIZE];   /* need not be aligned */
-
-   /*
-    * For good luck, apply the same static assertions as in backend's
-    * WriteControlFile().
-    */
-   StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE,
-                    "pg_control is too large for atomic disk writes");
-   StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE,
-                    "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE");
-
    /*
     * Adjust fields as needed to force an empty XLOG starting at
     * newXlogSegNo.
@@ -961,53 +948,8 @@ RewriteControlFile(void)
    ControlFile.max_prepared_xacts = 0;
    ControlFile.max_locks_per_xact = 64;
 
-   /* Contents are protected with a CRC */
-   INIT_CRC32C(ControlFile.crc);
-   COMP_CRC32C(ControlFile.crc,
-               (char *) &ControlFile,
-               offsetof(ControlFileData, crc));
-   FIN_CRC32C(ControlFile.crc);
-
-   /*
-    * We write out PG_CONTROL_FILE_SIZE bytes into pg_control, zero-padding
-    * the excess over sizeof(ControlFileData).  This reduces the odds of
-    * premature-EOF errors when reading pg_control.  We'll still fail when we
-    * check the contents of the file, but hopefully with a more specific
-    * error than "couldn't read pg_control".
-    */
-   memset(buffer, 0, PG_CONTROL_FILE_SIZE);
-   memcpy(buffer, &ControlFile, sizeof(ControlFileData));
-
-   unlink(XLOG_CONTROL_FILE);
-
-   fd = open(XLOG_CONTROL_FILE,
-             O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-             pg_file_create_mode);
-   if (fd < 0)
-   {
-       fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
-               progname, strerror(errno));
-       exit(1);
-   }
-
-   errno = 0;
-   if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
-   {
-       /* if write didn't set errno, assume problem is no disk space */
-       if (errno == 0)
-           errno = ENOSPC;
-       fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
-               progname, strerror(errno));
-       exit(1);
-   }
-
-   if (fsync(fd) != 0)
-   {
-       fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
-       exit(1);
-   }
-
-   close(fd);
+   /* The control file gets flushed here. */
+   update_controlfile(".", progname, &ControlFile, true);
 }
 
 
index 7f1d6bf48af2134ab78ab4004cf87367043a555a..3dcadb9b40afb0f49aa7d905b3c04d82e26bc18e 100644 (file)
@@ -377,7 +377,7 @@ main(int argc, char **argv)
    ControlFile_new.minRecoveryPoint = endrec;
    ControlFile_new.minRecoveryPointTLI = endtli;
    ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
-   update_controlfile(datadir_target, progname, &ControlFile_new);
+   update_controlfile(datadir_target, progname, &ControlFile_new, do_sync);
 
    pg_log(PG_PROGRESS, "syncing target data directory\n");
    syncTargetDirectory();
index 71e67a2edaa822df9f7c7c1627cb35629313093d..567281349e0768ec0d84ffc0ab0968d5d39db152 100644 (file)
@@ -29,7 +29,9 @@
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "port/pg_crc32c.h"
+
 #ifndef FRONTEND
+#include "pgstat.h"
 #include "storage/fd.h"
 #endif
 
@@ -144,13 +146,14 @@ get_controlfile(const char *DataDir, const char *progname, bool *crc_ok_p)
  * update_controlfile()
  *
  * Update controlfile values with the contents given by caller.  The
- * contents to write are included in "ControlFile".  Note that it is up
- * to the caller to fsync the updated file, and to properly lock
- * ControlFileLock when calling this routine in the backend.
+ * contents to write are included in "ControlFile". "do_sync" can be
+ * optionally used to flush the updated control file.  Note that it is up
+ * to the caller to properly lock ControlFileLock when calling this
+ * routine in the backend.
  */
 void
 update_controlfile(const char *DataDir, const char *progname,
-                  ControlFileData *ControlFile)
+                  ControlFileData *ControlFile, bool do_sync)
 {
    int         fd;
    char        buffer[PG_CONTROL_FILE_SIZE];
@@ -182,7 +185,12 @@ update_controlfile(const char *DataDir, const char *progname,
    snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s", DataDir, XLOG_CONTROL_FILE);
 
 #ifndef FRONTEND
-   if ((fd = OpenTransientFile(ControlFilePath, O_WRONLY | PG_BINARY)) == -1)
+
+   /*
+    * All errors issue a PANIC, so no need to use OpenTransientFile() and to
+    * worry about file descriptor leaks.
+    */
+   if ((fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY)) < 0)
        ereport(PANIC,
                (errcode_for_file_access(),
                 errmsg("could not open file \"%s\": %m",
@@ -198,6 +206,9 @@ update_controlfile(const char *DataDir, const char *progname,
 #endif
 
    errno = 0;
+#ifndef FRONTEND
+   pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE);
+#endif
    if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
    {
        /* if write didn't set errno, assume problem is no disk space */
@@ -215,19 +226,41 @@ update_controlfile(const char *DataDir, const char *progname,
        exit(EXIT_FAILURE);
 #endif
    }
+#ifndef FRONTEND
+   pgstat_report_wait_end();
+#endif
 
+   if (do_sync)
+   {
+#ifndef FRONTEND
+       pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE);
+       if (pg_fsync(fd) != 0)
+           ereport(PANIC,
+                   (errcode_for_file_access(),
+                    errmsg("could not fsync file \"%s\": %m",
+                           ControlFilePath)));
+       pgstat_report_wait_end();
+#else
+       if (fsync(fd) != 0)
+       {
+           fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
+                   progname, ControlFilePath, strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+#endif
+   }
+
+   if (close(fd) < 0)
+   {
 #ifndef FRONTEND
-   if (CloseTransientFile(fd))
        ereport(PANIC,
                (errcode_for_file_access(),
                 errmsg("could not close file \"%s\": %m",
                        ControlFilePath)));
 #else
-   if (close(fd) < 0)
-   {
        fprintf(stderr, _("%s: could not close file \"%s\": %s\n"),
                progname, ControlFilePath, strerror(errno));
        exit(EXIT_FAILURE);
-   }
 #endif
+   }
 }
index 95317ebacf293fc3d7d4904483895d62d531c8af..750ba6516491750370facd7a5297c38e46ae7910 100644 (file)
@@ -13,9 +13,9 @@
 #include "catalog/pg_control.h"
 
 extern ControlFileData *get_controlfile(const char *DataDir,
-                                       const char *progname,
-                                       bool *crc_ok_p);
+               const char *progname,
+               bool *crc_ok_p);
 extern void update_controlfile(const char *DataDir, const char *progname,
-                              ControlFileData *ControlFile);
+                  ControlFileData *ControlFile, bool do_sync);
 
 #endif                         /* COMMON_CONTROLDATA_UTILS_H */