Make archive recovery always start a new timeline, rather than only when a
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 29 Sep 2007 01:36:19 +0000 (01:36 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 29 Sep 2007 01:36:19 +0000 (01:36 +0000)
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.

doc/src/sgml/backup.sgml
src/backend/access/transam/xlog.c

index f19b636036e366207aed6a50b8519effa918b34c..8cfc64d9cb773536992d9f481aabeb1c177d2cec 100644 (file)
@@ -1134,11 +1134,9 @@ restore_command = 'copy /mnt/server/archivedir/%f "%p"'  # Windows
 
    <para>
     To deal with these problems, <productname>PostgreSQL</> has a notion
-    of <firstterm>timelines</>.  Each time you recover to a point-in-time
-    earlier than the end of the WAL sequence, a new timeline is created
-    to identify the series of WAL records generated after that recovery.
-    (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
+    of <firstterm>timelines</>.  Whenever an archive recovery is completed,
+    a new timeline is created to identify the series of WAL records
+    generated after that recovery.  The timeline
     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
     in fact possible to archive many different timelines.  While that might
index b58e3be337fdbb916849c789fe0f64098d64e6fa..5b5793fbdbab5b815b2ec79590d1c2e021b835ad 100644 (file)
@@ -4468,7 +4468,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
         *
         * 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
-        * of overwriting any existing file.
+        * of overwriting any existing file.  (This is, in fact, always the case
+        * at present.)
         */
        snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG");
        XLogFilePath(xlogpath, ThisTimeLineID, endLogId, endLogSeg);
@@ -4643,7 +4644,7 @@ StartupXLOG(void)
        XLogCtlInsert *Insert;
        CheckPoint      checkPoint;
        bool            wasShutdown;
-       bool            needNewTimeLine = false;
+       bool            reachedStopPoint = false;
        bool            haveBackupLabel = false;
        XLogRecPtr      RecPtr,
                                LastRec,
@@ -4958,7 +4959,7 @@ StartupXLOG(void)
                                 */
                                if (recoveryStopsHere(record, &recoveryApply))
                                {
-                                       needNewTimeLine = true;         /* see below */
+                                       reachedStopPoint = true;                /* see below */
                                        recoveryContinue = false;
                                        if (!recoveryApply)
                                                break;
@@ -5022,11 +5023,10 @@ StartupXLOG(void)
         */
        if (XLByteLT(EndOfLog, ControlFile->minRecoveryPoint))
        {
-               if (needNewTimeLine)    /* stopped because of stop request */
+               if (reachedStopPoint)   /* stopped because of stop request */
                        ereport(FATAL,
                                        (errmsg("requested recovery stop point is before end time of backup dump")));
-               else
-                       /* ran off end of WAL */
+               else                                    /* ran off end of WAL */
                        ereport(FATAL,
                                        (errmsg("WAL ends before end time of backup dump")));
        }
@@ -5034,12 +5034,18 @@ StartupXLOG(void)
        /*
         * Consider whether we need to assign a new timeline ID.
         *
-        * If we stopped short of the end of WAL during recovery, then we are
-        * generating a new timeline and must assign it a unique new ID.
-        * Otherwise, we can just extend the timeline we were in when we ran out
-        * of WAL.
+        * If we are doing an archive recovery, we always assign a new ID.  This
+        * handles a couple of issues.  If we stopped short of the end of WAL
+        * during recovery, then we are clearly generating a new timeline and must
+        * 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;
                ereport(LOG,