Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5adafaf

Browse files
committed
Have pg_rewind run crash recovery 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

+15
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

+80
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
}
174181

@@ -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)