]> granicus.if.org Git - postgresql/commitdiff
pg_rewind: fsync target data directory.
authorAndres Freund <andres@anarazel.de>
Sun, 27 Mar 2016 21:46:25 +0000 (23:46 +0200)
committerAndres Freund <andres@anarazel.de>
Sun, 27 Mar 2016 21:46:25 +0000 (23:46 +0200)
Previously pg_rewind did not fsync any files. That's problematic, given
that the target directory is modified. If the database was started
afterwards, 2ce439f33 luckily already caused the data directory to be
synced to disk at postmaster startup; reducing the scope of the problem.

To fix, use initdb -S, at the end of the pg_rewind run. It doesn't seem
worthwhile to duplicate the code into pg_rewind, and initdb -S is
already used that way by pg_upgrade.

Reported-By: Andres Freund
Author: Michael Paquier, somewhat edited by me
Discussion: 20160310034352.iuqgvpmg5qmnxtkz@alap3.anarazel.de
    CAB7nPqSytVG1o4S3S2pA1O=692ekurJ+fckW2PywEG3sNw54Ow@mail.gmail.com
Backpatch: 9.5, where pg_rewind was introduced

src/bin/pg_rewind/file_ops.c
src/bin/pg_rewind/pg_rewind.c

index 32eab3abc51f0aabea6ce0399bffffd2db2b1e3e..e88cf629b400dab6beb71faeccee6c4d0fb69b87 100644 (file)
@@ -79,7 +79,6 @@ close_target_file(void)
                                 dstpath, strerror(errno));
 
        dstfd = -1;
-       /* fsync? */
 }
 
 void
index c5589b9bb3a4e7e7709e9995d3abaeaf8c3f5a9d..4c80000ceaea59b60180775eed7bbe8e37384642 100644 (file)
@@ -36,6 +36,7 @@ static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli,
 static void digestControlFile(ControlFileData *ControlFile, char *source,
                                  size_t size);
 static void updateControlFile(ControlFileData *ControlFile);
+static void syncTargetDirectory(const char *argv0);
 static void sanityChecks(void);
 static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
 
@@ -349,6 +350,9 @@ main(int argc, char **argv)
        ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
        updateControlFile(&ControlFile_new);
 
+       pg_log(PG_PROGRESS, "syncing target data directory\n");
+       syncTargetDirectory(argv[0]);
+
        printf(_("Done!\n"));
 
        return 0;
@@ -650,3 +654,56 @@ updateControlFile(ControlFileData *ControlFile)
 
        close_target_file();
 }
+
+/*
+ * Sync target data directory to ensure that modifications are safely on disk.
+ *
+ * We do this once, for the whole data directory, for performance reasons.  At
+ * the end of pg_rewind's run, the kernel is likely to already have flushed
+ * most dirty buffers to disk. Additionally initdb -S uses a two-pass approach
+ * (only initiating writeback in the first pass), which often reduces the
+ * overall amount of IO noticeably.
+ */
+static void
+syncTargetDirectory(const char *argv0)
+{
+       int             ret;
+#define MAXCMDLEN (2 * MAXPGPATH)
+       char    exec_path[MAXPGPATH];
+       char    cmd[MAXCMDLEN];
+
+       /* locate initdb binary */
+       if ((ret = find_other_exec(argv0, "initdb",
+                                                          "initdb (PostgreSQL) " PG_VERSION "\n",
+                                                          exec_path)) < 0)
+       {
+               char        full_path[MAXPGPATH];
+
+               if (find_my_exec(argv0, full_path) < 0)
+                       strlcpy(full_path, progname, sizeof(full_path));
+
+               if (ret == -1)
+                       pg_fatal("The program \"initdb\" is needed by %s but was \n"
+                                        "not found in the same directory as \"%s\".\n"
+                                        "Check your installation.\n", progname, full_path);
+               else
+                       pg_fatal("The program \"initdb\" was found by \"%s\"\n"
+                                        "but was not the same version as %s.\n"
+                                        "Check your installation.\n", full_path, progname);
+       }
+
+       /* only skip processing after ensuring presence of initdb */
+       if (dry_run)
+               return;
+
+       /* finally run initdb -S */
+       if (debug)
+               snprintf(cmd, MAXCMDLEN, "\"%s\" -D \"%s\" -S",
+                                exec_path, datadir_target);
+       else
+               snprintf(cmd, MAXCMDLEN, "\"%s\" -D \"%s\" -S > \"%s\"",
+                                exec_path, datadir_target, DEVNULL);
+
+       if (system(cmd) != 0)
+               pg_fatal("sync of target directory failed\n");
+}