Skip to content

Commit c5b5168

Browse files
committed
pg_rewind: fsync target data directory.
Previously pg_rewind did not fsync any files. That's problematic, given that the target directory is modified. If the database was started afterwards, 2ce439f 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
1 parent fa5098d commit c5b5168

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

src/bin/pg_rewind/file_ops.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ close_target_file(void)
7979
dstpath, strerror(errno));
8080

8181
dstfd = -1;
82-
/* fsync? */
8382
}
8483

8584
void

src/bin/pg_rewind/pg_rewind.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli,
3636
static void digestControlFile(ControlFileData *ControlFile, char *source,
3737
size_t size);
3838
static void updateControlFile(ControlFileData *ControlFile);
39+
static void syncTargetDirectory(const char *argv0);
3940
static void sanityChecks(void);
4041
static void findCommonAncestorTimeline(XLogRecPtr *recptr, TimeLineID *tli);
4142

@@ -343,6 +344,9 @@ main(int argc, char **argv)
343344
ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
344345
updateControlFile(&ControlFile_new);
345346

347+
pg_log(PG_PROGRESS, "syncing target data directory\n");
348+
syncTargetDirectory(argv[0]);
349+
346350
printf(_("Done!\n"));
347351

348352
return 0;
@@ -572,3 +576,56 @@ updateControlFile(ControlFileData *ControlFile)
572576

573577
close_target_file();
574578
}
579+
580+
/*
581+
* Sync target data directory to ensure that modifications are safely on disk.
582+
*
583+
* We do this once, for the whole data directory, for performance reasons. At
584+
* the end of pg_rewind's run, the kernel is likely to already have flushed
585+
* most dirty buffers to disk. Additionally initdb -S uses a two-pass approach
586+
* (only initiating writeback in the first pass), which often reduces the
587+
* overall amount of IO noticeably.
588+
*/
589+
static void
590+
syncTargetDirectory(const char *argv0)
591+
{
592+
int ret;
593+
#define MAXCMDLEN (2 * MAXPGPATH)
594+
char exec_path[MAXPGPATH];
595+
char cmd[MAXCMDLEN];
596+
597+
/* locate initdb binary */
598+
if ((ret = find_other_exec(argv0, "initdb",
599+
"initdb (PostgreSQL) " PG_VERSION "\n",
600+
exec_path)) < 0)
601+
{
602+
char full_path[MAXPGPATH];
603+
604+
if (find_my_exec(argv0, full_path) < 0)
605+
strlcpy(full_path, progname, sizeof(full_path));
606+
607+
if (ret == -1)
608+
pg_fatal("The program \"initdb\" is needed by %s but was \n"
609+
"not found in the same directory as \"%s\".\n"
610+
"Check your installation.\n", progname, full_path);
611+
else
612+
pg_fatal("The program \"initdb\" was found by \"%s\"\n"
613+
"but was not the same version as %s.\n"
614+
"Check your installation.\n", full_path, progname);
615+
}
616+
617+
/* only skip processing after ensuring presence of initdb */
618+
if (dry_run)
619+
return;
620+
621+
/* finally run initdb -S */
622+
if (debug)
623+
snprintf(cmd, MAXCMDLEN, "\"%s\" -D \"%s\" -S",
624+
exec_path, datadir_target);
625+
else
626+
snprintf(cmd, MAXCMDLEN, "\"%s\" -D \"%s\" -S > \"%s\"",
627+
exec_path, datadir_target, DEVNULL);
628+
629+
if (system(cmd) != 0)
630+
pg_fatal("sync of target directory failed\n");
631+
}

0 commit comments

Comments
 (0)