From 22647fbce98364fd56460120f9759bdc0d93bd99 Mon Sep 17 00:00:00 2001 From: Paul Guo Date: Tue, 19 Mar 2019 12:41:30 +0800 Subject: [PATCH v6 3/3] Ensure target clean shutdown at the beginning of pg_rewind --- doc/src/sgml/ref/pg_rewind.sgml | 16 ++++++++ src/bin/pg_rewind/pg_rewind.c | 70 ++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml index 6a46b8c3b4..a7e001a864 100644 --- a/doc/src/sgml/ref/pg_rewind.sgml +++ b/doc/src/sgml/ref/pg_rewind.sgml @@ -176,6 +176,22 @@ PostgreSQL documentation + + + + + + By default, pg_rewind makes sure target is + clean shutdown before doing recovery by launching a single mode + postgres instance if needed to ensure the target has completed crash + recovery and pg_control is in clean shutdown state. Clean shutdown + state is a prerequisite of pg_rewind. + With this option, pg_rewind skips this and + then users need to do this themselves. + + + + diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index ddf71679b2..816f30365d 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -41,6 +41,7 @@ static void digestControlFile(ControlFileData *ControlFile, char *source, static void syncTargetDirectory(void); static void sanityChecks(void); static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex); +static void ensureCleanShutdown(const char *argv0); static ControlFileData ControlFile_target; static ControlFileData ControlFile_source; @@ -77,6 +78,7 @@ usage(const char *progname) printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n")); printf(_(" --source-server=CONNSTR source server to synchronize with\n")); printf(_(" -R, --write-recovery-conf write configuration for replication\n")); + printf(_(" -s, --skip-clean-shutdown skip running single-mode postgres if needed to make sure target is clean shutdown\n")); printf(_(" -n, --dry-run stop before modifying anything\n")); printf(_(" -N, --no-sync do not wait for changes to be written\n" " safely to disk\n")); @@ -103,6 +105,7 @@ main(int argc, char **argv) {"write-recovery-conf", no_argument, NULL, 'R'}, {"source-pgdata", required_argument, NULL, 1}, {"source-server", required_argument, NULL, 2}, + {"skip-clean-shutdown", no_argument, NULL, 's'}, {"version", no_argument, NULL, 'V'}, {"dry-run", no_argument, NULL, 'n'}, {"no-sync", no_argument, NULL, 'N'}, @@ -119,6 +122,7 @@ main(int argc, char **argv) XLogRecPtr chkptredo; size_t size; char *buffer; + bool skip_clean_shutdown = false; bool rewind_needed; XLogRecPtr endrec; TimeLineID endtli; @@ -144,7 +148,7 @@ main(int argc, char **argv) } } - while ((c = getopt_long(argc, argv, "D:nNPR", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "D:nNPRs", long_options, &option_index)) != -1) { switch (c) { @@ -152,6 +156,10 @@ main(int argc, char **argv) fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); + case 's': + skip_clean_shutdown = true; + break; + case 'P': showprogress = true; break; @@ -258,6 +266,22 @@ main(int argc, char **argv) digestControlFile(&ControlFile_target, buffer, size); pg_free(buffer); + /* + * If the target instance was not cleanly shut down, run a single-user + * postgres session really quickly and reload the control file to get the + * new state. + */ + if (!skip_clean_shutdown && + ControlFile_target.state != DB_SHUTDOWNED && + ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY) + { + ensureCleanShutdown(argv[0]); + + buffer = slurpFile(datadir_target, "global/pg_control", &size); + digestControlFile(&ControlFile_target, buffer, size); + pg_free(buffer); + } + buffer = fetchFile("global/pg_control", &size); digestControlFile(&ControlFile_source, buffer, size); pg_free(buffer); @@ -772,3 +796,47 @@ syncTargetDirectory(void) fsync_pgdata(datadir_target, PG_VERSION_NUM); } + +/* + * Ensure clean shutdown of target instance by launching single-user mode + * postgres to do crash recovery. + */ +static void +ensureCleanShutdown(const char *argv0) +{ + int ret; +#define MAXCMDLEN (2 * MAXPGPATH) + char exec_path[MAXPGPATH]; + char cmd[MAXCMDLEN]; + + /* locate postgres binary */ + if ((ret = find_other_exec(argv0, "postgres", + PG_BACKEND_VERSIONSTR, + 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 \"postgres\" 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 \"postgres\" 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 postgres */ + if (dry_run) + return; + + /* finally run postgres single-user mode */ + snprintf(cmd, MAXCMDLEN, "\"%s\" --single -D \"%s\" template1 < %s", + exec_path, datadir_target, DEVNULL); + + if (system(cmd) != 0) + pg_fatal("postgres single-user mode of target instance failed for command: %s\n", cmd); +} -- 2.17.2