8000 Have pg_rewind run crash recovery before rewinding · postgres/postgres@5adafaf · GitHub
[go: up one dir, main page]

Skip to content

Commit 5adafaf

Browse files
committed
Have pg_rewind run crash rec 10000 overy before rewinding
If we don't do this, the rewind fails if the server wasn't cleanly shut down, which seems unhelpful serving no purpose. Also provide a new option --no-ensure-shutdown to suppress this behavior, for alleged advanced usage that prefers to avoid the crash recovery. Authors: Paul Guo, Jimmy Yih, Ashwin Agrawal Reviewed-by: Álvaro Herrera Discussion: https://postgr.es/m/CAEET0ZEffUkXc48pg2iqARQgGRYDiiVxDu+yYek_bTwJF+q=Uw@mail.gmail.com
1 parent c967e13 commit 5adafaf

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

doc/src/sgml/ref/pg_rewind.sgml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ PostgreSQL documentation
165165
</listitem>
166166
</varlistentry>
167167

168+
<varlistentry>
169+
<term><option>--no-ensure-shutdown</option></term>
170+
<listitem>
171+
<para>
172+
<application>pg_rewind</application> verifies that the target server
173+
is cleanly shutdown before rewinding; by default, if it isn't, it
174+
starts the server in single-user mode to complete crash recovery.
175+
By passing this option, <application>pg_rewind</application> skips
176+
this and errors out immediately if the server is not cleanly shut
177+
down. Users are expected to handle the situation themselves in that
178+
case.
179+
</para>
180+
</listitem>
181+
</varlistentry>
182+
168183
<varlistentry>
169184
<term><option>-n</option></term>
170185
<term><option>--dry-run</option></term>

src/bin/pg_rewind/pg_rewind.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static void digestControlFile(ControlFileData *ControlFile, char *source,
4040
static void syncTargetDirectory(void);
4141
static void sanityChecks(void);
4242
static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
43+
static void ensureCleanShutdown(const char *argv0);
4344

4445
static ControlFileData ControlFile_target;
4546
static ControlFileData ControlFile_source;
@@ -79,6 +80,7 @@ usage(const char *progname)
7980
printf(_(" -N, --no-sync do not wait for changes to be written\n"
8081
" safely to disk\n"));
8182
printf(_(" -P, --progress write progress messages\n"));
83+
printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
8284
printf(_(" --debug write a lot of debug messages\n"));
8385
printf(_(" -V, --version output version information, then exit\n"));
8486
printf(_(" -?, --help show this help, then exit\n"));
@@ -94,6 +96,7 @@ main(int argc, char **argv)
9496
{"target-pgdata", required_argument, NULL, 'D'},
9597
{"source-pgdata", required_argument, NULL, 1},
9698
{"source-server", required_argument, NULL, 2},
99+
{"no-ensure-shutdown", no_argument, NULL, 44},
97100
{"version", no_argument, NULL, 'V'},
98101
{"dry-run", no_argument, NULL, 'n'},
99102
{"no-sync", no_argument, NULL, 'N'},
@@ -110,6 +113,7 @@ main(int argc, char **argv)
110113
XLogRecPtr chkptredo;
111114
size_t size;
112115
char *buffer;
116+
bool no_ensure_shutdown = false;
113117
bool rewind_needed;
114118
XLogRecPtr endrec;
115119
TimeLineID endtli;
@@ -169,6 +173,9 @@ main(int argc, char **argv)
169173
case 2: /* --source-server */
170174
connstr_source = pg_strdup(optarg);
171175
break;
176+
case 4:
177+
no_ensure_shutdown = true;
178+
break;
172179
}
173180
}
17 10000 4181

@@ -241,6 +248,24 @@ main(int argc, char **argv)
241248
digestControlFile(&ControlFile_target, buffer, size);
242249
pg_free(buffer);
243250

251+
/*
252+
* If the target instance was not cleanly shut down, run a single-user
253+
* postgres session really quickly and reload the control file to get the
254+
* new state. Note if no_ensure_shutdown is specified, pg_rewind won't do
255+
* that automatically. That means users need to do themselves in advance,
256+
* else pg_rewind will soon quit, see sanityChecks().
257+
*/
258+
if (!no_ensure_shutdown &&
259+
ControlFile_target.state != DB_SHUTDOWNED &&
260+
ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
261+
{
262+
ensureCleanShutdown(argv[0]);
263+
264+
buffer = slurpFile(datadir_target, "global/pg_control", &size);
265+
digestControlFile(&ControlFile_target, buffer, size);
266+
pg_free(buffer);
267+
}
268+
244269
buffer = fetchFile("global/pg_control", &size);
245270
digestControlFile(&ControlFile_source, buffer, size);
246271
pg_free(buffer);
@@ -748,3 +773,58 @@ syncTargetDirectory(void)
748773

749774
fsync_pgdata(datadir_target, PG_VERSION_NUM);
750775
}
776+
777+
/*
778+
* Ensure clean shutdown of target instance by launching single-user mode
779+
* postgres to do crash recovery.
780+
*/
781+
static void
782+
ensureCleanShutdown(const char *argv0)
783+
{
784+
int ret;
785+
#define MAXCMDLEN (2 * MAXPGPATH)
786+
char exec_path[MAXPGPATH];
787+
char cmd[MAXCMDLEN];
788+
789+
/* locate postgres binary */
790+
if ((ret = find_other_exec(argv0, "postgres",
791+
PG_BACKEND_VERSIONSTR,
792+
exec_path)) < 0)
793+
{
794+
char full_path[MAXPGPATH];
795+
796+
if (find_my_exec(argv0, full_path) < 0)
797+
strlcpy(full_path, progname, sizeof(full_path));
798+
799+
if (ret == -1)
800+
pg_fatal("The program \"%s\" is needed by %s but was\n"
801+
"not found in the same directory as \"%s\".\n"
802+
"Check your installation.",
803+
"postgres", progname, full_path);
804+
else
805+
pg_fatal("The program \"%s\" was found by \"%s\" but was\n"
806+
"not the same version as %s.\n"
807+
"Check your installation.",
808+
"postgres", full_path, progname);
809+
}
810+
811+
pg_log_info("executing \"%s\" for target server to complete crash recovery",
812+
exec_path);
813+
814+
/*
815+
* Skip processing if requested, but only after ensuring presence of
816+
* postgres.
817+
*/
818+
if (dry_run)
819+
return;
820+
821+
/* finally run postgres in single-user mode */
822+
snprintf(cmd, MAXCMDLEN, "\"%s\" --single -D \"%s\" template1 < \"%s\"",
823+
exec_path, datadir_target, DEVNULL);
824+
825+
if (system(cmd) != 0)
826+
{
827+
pg_log_error("postgres single-user mode of target instance failed");
828+
pg_fatal("Command was: %s", cmd);
829+
}
830+
}

0 commit comments

Comments
 (0)
0