Make archive recovery always start a new timeline, rather than only when a

recovery stop time was used.  This avoids a corner-case risk of trying to
overwrite an existing archived copy of the last WAL segment, and seems
simpler and cleaner all around than the original definition.  Per example
from Jon Colverson and subsequent analysis by Simon.
This commit is contained in:
Tom Lane 2007-09-29 01:36:10 +00:00
parent b37e1770c5
commit b46bd55a6c
2 changed files with 22 additions and 18 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.102 2007/09/26 22:36:30 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.103 2007/09/29 01:36:10 tgl Exp $ -->
<chapter id="backup"> <chapter id="backup">
<title>Backup and Restore</title> <title>Backup and Restore</title>
@ -1174,11 +1174,9 @@ restore_command = 'copy /mnt/server/archivedir/%f "%p"' # Windows
<para> <para>
To deal with these problems, <productname>PostgreSQL</> has a notion To deal with these problems, <productname>PostgreSQL</> has a notion
of <firstterm>timelines</>. Each time you recover to a point-in-time of <firstterm>timelines</>. Whenever an archive recovery is completed,
earlier than the end of the WAL sequence, a new timeline is created a new timeline is created to identify the series of WAL records
to identify the series of WAL records generated after that recovery. generated after that recovery. The timeline
(If recovery proceeds all the way to the end of WAL, however, we do not
start a new timeline: we just extend the existing one.) The timeline
ID number is part of WAL segment file names, and so a new timeline does ID number is part of WAL segment file names, and so a new timeline does
not overwrite the WAL data generated by previous timelines. It is not overwrite the WAL data generated by previous timelines. It is
in fact possible to archive many different timelines. While that might in fact possible to archive many different timelines. While that might

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, 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.282 2007/09/26 22:36:30 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.283 2007/09/29 01:36:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -4518,7 +4518,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
* *
* Note that if we are establishing a new timeline, ThisTimeLineID is * Note that if we are establishing a new timeline, ThisTimeLineID is
* already set to the new value, and so we will create a new file instead * already set to the new value, and so we will create a new file instead
* of overwriting any existing file. * of overwriting any existing file. (This is, in fact, always the case
* at present.)
*/ */
snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG"); snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG");
XLogFilePath(xlogpath, ThisTimeLineID, endLogId, endLogSeg); XLogFilePath(xlogpath, ThisTimeLineID, endLogId, endLogSeg);
@ -4700,7 +4701,7 @@ StartupXLOG(void)
XLogCtlInsert *Insert; XLogCtlInsert *Insert;
CheckPoint checkPoint; CheckPoint checkPoint;
bool wasShutdown; bool wasShutdown;
bool needNewTimeLine = false; bool reachedStopPoint = false;
bool haveBackupLabel = false; bool haveBackupLabel = false;
XLogRecPtr RecPtr, XLogRecPtr RecPtr,
LastRec, LastRec,
@ -5010,7 +5011,7 @@ StartupXLOG(void)
*/ */
if (recoveryStopsHere(record, &recoveryApply)) if (recoveryStopsHere(record, &recoveryApply))
{ {
needNewTimeLine = true; /* see below */ reachedStopPoint = true; /* see below */
recoveryContinue = false; recoveryContinue = false;
if (!recoveryApply) if (!recoveryApply)
break; break;
@ -5078,11 +5079,10 @@ StartupXLOG(void)
*/ */
if (XLByteLT(EndOfLog, ControlFile->minRecoveryPoint)) if (XLByteLT(EndOfLog, ControlFile->minRecoveryPoint))
{ {
if (needNewTimeLine) /* stopped because of stop request */ if (reachedStopPoint) /* stopped because of stop request */
ereport(FATAL, ereport(FATAL,
(errmsg("requested recovery stop point is before end time of backup dump"))); (errmsg("requested recovery stop point is before end time of backup dump")));
else else /* ran off end of WAL */
/* ran off end of WAL */
ereport(FATAL, ereport(FATAL,
(errmsg("WAL ends before end time of backup dump"))); (errmsg("WAL ends before end time of backup dump")));
} }
@ -5090,12 +5090,18 @@ StartupXLOG(void)
/* /*
* Consider whether we need to assign a new timeline ID. * Consider whether we need to assign a new timeline ID.
* *
* If we stopped short of the end of WAL during recovery, then we are * If we are doing an archive recovery, we always assign a new ID. This
* generating a new timeline and must assign it a unique new ID. * handles a couple of issues. If we stopped short of the end of WAL
* Otherwise, we can just extend the timeline we were in when we ran out * during recovery, then we are clearly generating a new timeline and must
* of WAL. * assign it a unique new ID. Even if we ran to the end, modifying the
* current last segment is problematic because it may result in trying
* to overwrite an already-archived copy of that segment, and we encourage
* DBAs to make their archive_commands reject that. We can dodge the
* problem by making the new active segment have a new timeline ID.
*
* In a normal crash recovery, we can just extend the timeline we were in.
*/ */
if (needNewTimeLine) if (InArchiveRecovery)
{ {
ThisTimeLineID = findNewestTimeLine(recoveryTargetTLI) + 1; ThisTimeLineID = findNewestTimeLine(recoveryTargetTLI) + 1;
ereport(LOG, ereport(LOG,