Commit c21ac0b5 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Add restartpoint_command option to recovery.conf. Fix bug in %r handling

in recovery_end_command, it always came out as 0 because InRedo was
cleared before recovery_end_command was executed. Also, always take
ControlFileLock when reading checkpoint location for %r.

The recovery_end_command bug and the missing locking was present in 8.4
as well, that part of this patch will be backported separately.
parent 6407fa99
<!-- $PostgreSQL: pgsql/doc/src/sgml/recovery-config.sgml,v 2.2 2010/02/25 09:32:19 heikki Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/recovery-config.sgml,v 2.3 2010/03/18 09:17:18 heikki Exp $ -->
<chapter Id="recovery-config"> <chapter Id="recovery-config">
<title>Recovery Configuration</title> <title>Recovery Configuration</title>
...@@ -59,14 +59,15 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows ...@@ -59,14 +59,15 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="recovery-end-command" xreflabel="recovery_end_command"> <varlistentry id="restartpoint-command" xreflabel="restartpoint_command">
<term><varname>recovery_end_command</varname> (<type>string</type>)</term> <term><varname>restartpoint_command</varname> (<type>string</type>)</term>
<listitem> <listitem>
<para> <para>
This parameter specifies a shell command that will be executed once only This parameter specifies a shell command that will be executed at
at the end of recovery. This parameter is optional. The purpose of the every restartpoint. This parameter is optional. The purpose of the
<varname>recovery_end_command</> is to provide a mechanism for cleanup <varname>restartpoint_command</> is to provide a mechanism for cleaning
following replication or recovery. up old archived WAL files that are no longer needed by the standby
server.
Any <literal>%r</> is replaced by the name of the file Any <literal>%r</> is replaced by the name of the file
containing the last valid restart point. That is the earliest file that containing the last valid restart point. That is the earliest file that
must be kept to allow a restore to be restartable, so this information must be kept to allow a restore to be restartable, so this information
...@@ -77,6 +78,24 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows ...@@ -77,6 +78,24 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
Write <literal>%%</> to embed an actual <literal>%</> character Write <literal>%%</> to embed an actual <literal>%</> character
in the command. in the command.
</para> </para>
<para>
If the command returns a non-zero exit status then a WARNING log
message will be written.
</para>
</listitem>
</varlistentry>
<varlistentry id="recovery-end-command" xreflabel="recovery_end_command">
<term><varname>recovery_end_command</varname> (<type>string</type>)</term>
<listitem>
<para>
This parameter specifies a shell command that will be executed once only
at the end of recovery. This parameter is optional. The purpose of the
<varname>recovery_end_command</> is to provide a mechanism for cleanup
following replication or recovery.
Any <literal>%r</> is replaced by the name of the file containing the
last valid restart point, like in <xref linkend="restartpoint-command">.
</para>
<para> <para>
If the command returns a non-zero exit status then a WARNING log If the command returns a non-zero exit status then a WARNING log
message will be written and the database will proceed to start up message will be written and the database will proceed to start up
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.381 2010/03/15 18:49:17 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.382 2010/03/18 09:17:18 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -171,6 +171,7 @@ static bool restoredFromArchive = false; ...@@ -171,6 +171,7 @@ static bool restoredFromArchive = false;
/* options taken from recovery.conf for archive recovery */ /* options taken from recovery.conf for archive recovery */
static char *recoveryRestoreCommand = NULL; static char *recoveryRestoreCommand = NULL;
static char *recoveryEndCommand = NULL; static char *recoveryEndCommand = NULL;
static char *restartPointCommand = NULL;
static bool recoveryTarget = false; static bool recoveryTarget = false;
static bool recoveryTargetExact = false; static bool recoveryTargetExact = false;
static bool recoveryTargetInclusive = true; static bool recoveryTargetInclusive = true;
...@@ -370,6 +371,11 @@ typedef struct XLogCtlData ...@@ -370,6 +371,11 @@ typedef struct XLogCtlData
int XLogCacheBlck; /* highest allocated xlog buffer index */ int XLogCacheBlck; /* highest allocated xlog buffer index */
TimeLineID ThisTimeLineID; TimeLineID ThisTimeLineID;
TimeLineID RecoveryTargetTLI; TimeLineID RecoveryTargetTLI;
/*
* restartPointCommand is read from recovery.conf but needs to be in
* shared memory so that the bgwriter process can access it.
*/
char restartPointCommand[MAXPGPATH];
/* /*
* SharedRecoveryInProgress indicates if we're still in crash or archive * SharedRecoveryInProgress indicates if we're still in crash or archive
...@@ -520,7 +526,8 @@ static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt, ...@@ -520,7 +526,8 @@ static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt,
static void XLogFileClose(void); static void XLogFileClose(void);
static bool RestoreArchivedFile(char *path, const char *xlogfname, static bool RestoreArchivedFile(char *path, const char *xlogfname,
const char *recovername, off_t expectedSize); const char *recovername, off_t expectedSize);
static void ExecuteRecoveryEndCommand(void); static void ExecuteRecoveryCommand(char *command, char *commandName,
bool failOnerror);
static void PreallocXlogFiles(XLogRecPtr endptr); static void PreallocXlogFiles(XLogRecPtr endptr);
static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr); static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr);
static void ValidateXLOGDirectoryStructure(void); static void ValidateXLOGDirectoryStructure(void);
...@@ -2990,12 +2997,19 @@ not_available: ...@@ -2990,12 +2997,19 @@ not_available:
} }
/* /*
* Attempt to execute the recovery_end_command. * Attempt to execute an external shell command during recovery.
*
* 'command' is the shell command to be executed, 'commandName' is a
* human-readable name describing the command emitted in the logs. If
* 'failonSignal' is true and the command is killed by a signal, a FATAL
* error is thrown. Otherwise a WARNING is emitted.
*
* This is currently used for restore_end_command and restartpoint_command.
*/ */
static void static void
ExecuteRecoveryEndCommand(void) ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
{ {
char xlogRecoveryEndCmd[MAXPGPATH]; char xlogRecoveryCmd[MAXPGPATH];
char lastRestartPointFname[MAXPGPATH]; char lastRestartPointFname[MAXPGPATH];
char *dp; char *dp;
char *endp; char *endp;
...@@ -3005,43 +3019,29 @@ ExecuteRecoveryEndCommand(void) ...@@ -3005,43 +3019,29 @@ ExecuteRecoveryEndCommand(void)
uint32 restartLog; uint32 restartLog;
uint32 restartSeg; uint32 restartSeg;
Assert(recoveryEndCommand); Assert(command && commandName);
/* /*
* Calculate the archive file cutoff point for use during log shipping * Calculate the archive file cutoff point for use during log shipping
* replication. All files earlier than this point can be deleted from the * replication. All files earlier than this point can be deleted from the
* archive, though there is no requirement to do so. * archive, though there is no requirement to do so.
*
* We initialise this with the filename of an InvalidXLogRecPtr, which
* will prevent the deletion of any WAL files from the archive because of
* the alphabetic sorting property of WAL filenames.
*
* Once we have successfully located the redo pointer of the checkpoint
* from which we start recovery we never request a file prior to the redo
* pointer of the last restartpoint. When redo begins we know that we have
* successfully located it, so there is no need for additional status
* flags to signify the point when we can begin deleting WAL files from
* the archive.
*/ */
if (InRedo) LWLockAcquire(ControlFileLock, LW_SHARED);
{ XLByteToSeg(ControlFile->checkPointCopy.redo,
XLByteToSeg(ControlFile->checkPointCopy.redo, restartLog, restartSeg);
restartLog, restartSeg); XLogFileName(lastRestartPointFname,
XLogFileName(lastRestartPointFname, ControlFile->checkPointCopy.ThisTimeLineID,
ControlFile->checkPointCopy.ThisTimeLineID, restartLog, restartSeg);
restartLog, restartSeg); LWLockRelease(ControlFileLock);
}
else
XLogFileName(lastRestartPointFname, 0, 0, 0);
/* /*
* construct the command to be executed * construct the command to be executed
*/ */
dp = xlogRecoveryEndCmd; dp = xlogRecoveryCmd;
endp = xlogRecoveryEndCmd + MAXPGPATH - 1; endp = xlogRecoveryCmd + MAXPGPATH - 1;
*endp = '\0'; *endp = '\0';
for (sp = recoveryEndCommand; *sp; sp++) for (sp = command; *sp; sp++)
{ {
if (*sp == '%') if (*sp == '%')
{ {
...@@ -3075,13 +3075,12 @@ ExecuteRecoveryEndCommand(void) ...@@ -3075,13 +3075,12 @@ ExecuteRecoveryEndCommand(void)
*dp = '\0'; *dp = '\0';
ereport(DEBUG3, ereport(DEBUG3,
(errmsg_internal("executing recovery end command \"%s\"", (errmsg_internal("executing %s \"%s\"", commandName, command)));
xlogRecoveryEndCmd)));
/* /*
* execute the constructed command * execute the constructed command
*/ */
rc = system(xlogRecoveryEndCmd); rc = system(xlogRecoveryCmd);
if (rc != 0) if (rc != 0)
{ {
/* /*
...@@ -3091,9 +3090,13 @@ ExecuteRecoveryEndCommand(void) ...@@ -3091,9 +3090,13 @@ ExecuteRecoveryEndCommand(void)
*/ */
signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125; signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125;
ereport(signaled ? FATAL : WARNING, /*
(errmsg("recovery_end_command \"%s\": return code %d", * translator: First %s represents a recovery.conf parameter name like
xlogRecoveryEndCmd, rc))); * "recovery_end_command", and the 2nd is the value of that parameter.
*/
ereport((signaled && failOnSignal) ? FATAL : WARNING,
(errmsg("%s \"%s\": return code %d", commandName,
command, rc)));
} }
} }
...@@ -4936,6 +4939,13 @@ readRecoveryCommandFile(void) ...@@ -4936,6 +4939,13 @@ readRecoveryCommandFile(void)
(errmsg("recovery_end_command = '%s'", (errmsg("recovery_end_command = '%s'",
recoveryEndCommand))); recoveryEndCommand)));
} }
else if (strcmp(tok1, "restartpoint_command") == 0)
{
restartPointCommand = pstrdup(tok2);
ereport(DEBUG2,
(errmsg("restartpoint_command = '%s'",
restartPointCommand)));
}
else if (strcmp(tok1, "recovery_target_timeline") == 0) else if (strcmp(tok1, "recovery_target_timeline") == 0)
{ {
rtliGiven = true; rtliGiven = true;
...@@ -5505,8 +5515,14 @@ StartupXLOG(void) ...@@ -5505,8 +5515,14 @@ StartupXLOG(void)
recoveryTargetTLI, recoveryTargetTLI,
ControlFile->checkPointCopy.ThisTimeLineID))); ControlFile->checkPointCopy.ThisTimeLineID)));
/* Save the selected recovery target timeline ID in shared memory */ /*
* Save the selected recovery target timeline ID and restartpoint_command
* in shared memory so that other processes can see them
*/
XLogCtl->RecoveryTargetTLI = recoveryTargetTLI; XLogCtl->RecoveryTargetTLI = recoveryTargetTLI;
strncpy(XLogCtl->restartPointCommand,
restartPointCommand ? restartPointCommand : "",
sizeof(XLogCtl->restartPointCommand));
if (read_backup_label(&checkPointLoc)) if (read_backup_label(&checkPointLoc))
{ {
...@@ -6129,7 +6145,9 @@ StartupXLOG(void) ...@@ -6129,7 +6145,9 @@ StartupXLOG(void)
* And finally, execute the recovery_end_command, if any. * And finally, execute the recovery_end_command, if any.
*/ */
if (recoveryEndCommand) if (recoveryEndCommand)
ExecuteRecoveryEndCommand(); ExecuteRecoveryCommand(recoveryEndCommand,
"recovery_end_command",
true);
} }
/* /*
...@@ -7318,6 +7336,15 @@ CreateRestartPoint(int flags) ...@@ -7318,6 +7336,15 @@ CreateRestartPoint(int flags)
timestamptz_to_str(GetLatestXLogTime())))); timestamptz_to_str(GetLatestXLogTime()))));
LWLockRelease(CheckpointLock); LWLockRelease(CheckpointLock);
/*
* Finally, execute restartpoint_command, if any.
*/
if (XLogCtl->restartPointCommand[0])
ExecuteRecoveryCommand(XLogCtl->restartPointCommand,
"restartpoint_command",
false);
return true; return true;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment