]> granicus.if.org Git - postgresql/commitdiff
When two base backups are started at the same time with pg_basebackup,
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 21 Mar 2011 09:25:25 +0000 (11:25 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 21 Mar 2011 09:25:25 +0000 (11:25 +0200)
ensure that they use different checkpoints as the starting point. We use
the checkpoint redo location as a unique identifier for the base backup in
the end-of-backup record, and in the backup history file name.

Bug spotted by Fujii Masao.

src/backend/access/transam/xlog.c

index 45ba0013c8a2e21ba5ed645c37a88f022be3ab74..306ac058c365c15fd32deed7677aefe668bb6028 100644 (file)
@@ -355,10 +355,13 @@ typedef struct XLogCtlInsert
         * exclusiveBackup is true if a backup started with pg_start_backup() is
         * in progress, and nonExclusiveBackups is a counter indicating the number
         * of streaming base backups currently in progress. forcePageWrites is
-        * set to true when either of these is non-zero.
+        * set to true when either of these is non-zero. lastBackupStart is the
+        * latest checkpoint redo location used as a starting point for an online
+        * backup.
         */
        bool            exclusiveBackup;
        int                     nonExclusiveBackups;
+       XLogRecPtr      lastBackupStart;
 } XLogCtlInsert;
 
 /*
@@ -8808,6 +8811,19 @@ do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile)
                                 errmsg("backup label too long (max %d bytes)",
                                                MAXPGPATH)));
 
+       /*
+        * Force an XLOG file switch before the checkpoint, to ensure that the WAL
+        * segment the checkpoint is written to doesn't contain pages with old
+        * timeline IDs. That would otherwise happen if you called
+        * pg_start_backup() right after restoring from a PITR archive: the first
+        * WAL segment containing the startup checkpoint has pages in the
+        * beginning with the old timeline ID. That can cause trouble at recovery:
+        * we won't have a history file covering the old timeline if pg_xlog
+        * directory was not included in the base backup and the WAL archive was
+        * cleared too before starting the backup.
+        */
+       RequestXLogSwitch();
+
        /*
         * Mark backup active in shared memory.  We must do full-page WAL writes
         * during an on-line backup even if not doing so at other times, because
@@ -8843,43 +8859,54 @@ do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile)
        XLogCtl->Insert.forcePageWrites = true;
        LWLockRelease(WALInsertLock);
 
-       /*
-        * Force an XLOG file switch before the checkpoint, to ensure that the WAL
-        * segment the checkpoint is written to doesn't contain pages with old
-        * timeline IDs. That would otherwise happen if you called
-        * pg_start_backup() right after restoring from a PITR archive: the first
-        * WAL segment containing the startup checkpoint has pages in the
-        * beginning with the old timeline ID. That can cause trouble at recovery:
-        * we won't have a history file covering the old timeline if pg_xlog
-        * directory was not included in the base backup and the WAL archive was
-        * cleared too before starting the backup.
-        */
-       RequestXLogSwitch();
-
        /* Ensure we release forcePageWrites if fail below */
        PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
        {
-               /*
-                * Force a CHECKPOINT.  Aside from being necessary to prevent torn
-                * page problems, this guarantees that two successive backup runs will
-                * have different checkpoint positions and hence different history
-                * file names, even if nothing happened in between.
-                *
-                * We use CHECKPOINT_IMMEDIATE only if requested by user (via passing
-                * fast = true).  Otherwise this can take awhile.
-                */
-               RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |
-                                                 (fast ? CHECKPOINT_IMMEDIATE : 0));
+               bool gotUniqueStartpoint = false;
+               do
+               {
+                       /*
+                        * Force a CHECKPOINT.  Aside from being necessary to prevent torn
+                        * page problems, this guarantees that two successive backup runs will
+                        * have different checkpoint positions and hence different history
+                        * file names, even if nothing happened in between.
+                        *
+                        * We use CHECKPOINT_IMMEDIATE only if requested by user (via passing
+                        * fast = true).  Otherwise this can take awhile.
+                        */
+                       RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |
+                                                         (fast ? CHECKPOINT_IMMEDIATE : 0));
 
-               /*
-                * Now we need to fetch the checkpoint record location, and also its
-                * REDO pointer.  The oldest point in WAL that would be needed to
-                * restore starting from the checkpoint is precisely the REDO pointer.
-                */
-               LWLockAcquire(ControlFileLock, LW_SHARED);
-               checkpointloc = ControlFile->checkPoint;
-               startpoint = ControlFile->checkPointCopy.redo;
-               LWLockRelease(ControlFileLock);
+                       /*
+                        * Now we need to fetch the checkpoint record location, and also its
+                        * REDO pointer.  The oldest point in WAL that would be needed to
+                        * restore starting from the checkpoint is precisely the REDO pointer.
+                        */
+                       LWLockAcquire(ControlFileLock, LW_SHARED);
+                       checkpointloc = ControlFile->checkPoint;
+                       startpoint = ControlFile->checkPointCopy.redo;
+                       LWLockRelease(ControlFileLock);
+
+                       /*
+                        * If two base backups are started at the same time (in WAL
+                        * sender processes), we need to make sure that they use
+                        * different checkpoints as starting locations, because we use
+                        * the starting WAL location as a unique identifier for the base
+                        * backup in the end-of-backup WAL record and when we write the
+                        * backup history file. Perhaps it would be better generate a
+                        * separate unique ID for each backup instead of forcing another
+                        * checkpoint, but taking a checkpoint right after another is
+                        * not that expensive either because only few buffers have been
+                        * dirtied yet.
+                        */
+                       LWLockAcquire(WALInsertLock, LW_SHARED);
+                       if (XLByteLT(XLogCtl->Insert.lastBackupStart, startpoint))
+                       {
+                               XLogCtl->Insert.lastBackupStart = startpoint;
+                               gotUniqueStartpoint = true;
+                       }
+                       LWLockRelease(WALInsertLock);
+               } while(!gotUniqueStartpoint);
 
                XLByteToSeg(startpoint, _logId, _logSeg);
                XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg);