v9-0002-Ensure-target-clean-shutdown-in-pg_rewind.patch
application/octet-stream
Filename: v9-0002-Ensure-target-clean-shutdown-in-pg_rewind.patch
Type: application/octet-stream
Part: 1
Patch
Format: format-patch
Series: patch v9-0002
Subject: Ensure target clean shutdown in pg_rewind
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/pg_rewind.sgml | 16 | 0 |
| src/bin/pg_rewind/pg_rewind.c | 71 | 1 |
From 30082eab047e677a8b17ba255f512e04ca1f21ef Mon Sep 17 00:00:00 2001
From: Paul Guo <paulguo@gmail.com>
Date: Tue, 19 Mar 2019 12:41:30 +0800
Subject: [PATCH v9 2/2] Ensure target clean shutdown in pg_rewind
---
doc/src/sgml/ref/pg_rewind.sgml | 16 ++++++++
src/bin/pg_rewind/pg_rewind.c | 72 ++++++++++++++++++++++++++++++++-
2 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml
index b8b43e8220..aa269b3892 100644
--- a/doc/src/sgml/ref/pg_rewind.sgml
+++ b/doc/src/sgml/ref/pg_rewind.sgml
@@ -176,6 +176,22 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-s</option></term>
+ <term><option>--no-ensure-shutdown</option></term>
+ <listitem>
+ <para>
+ By default, <application>pg_rewind</application> makes sure target is
+ cleanly 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 <application>pg_rewind</application>.
+ With this option, <application>pg_rewind</application> skips this and
+ then users need to do this themselves.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index 6f1dadbceb..d41bb12ade 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;
@@ -83,6 +84,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, --no-ensure-shutdown do not automatically fix unclean 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},
+ {"no-ensure-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 no_ensure_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':
+ no_ensure_shutdown = true;
+ break;
+
case 'P':
showprogress = true;
break;
@@ -257,6 +265,24 @@ 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. Note if no_ensure_shutdown is specified, pg_rewind won't
+ * do that automatically. That means users need to do themselves in
+ * advance, else pg_rewind will soon quit, see sanityChecks().
+ */
+ if (!no_ensure_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);
@@ -771,3 +797,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