diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c045807dc35d3397ebdefa6cdbaab2cdb737acb4..9a13a98cbc1176fcc789e566add9ea1692b02563 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.356 2010/01/02 16:57:35 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.357 2010/01/04 12:50:49 heikki Exp $ * *------------------------------------------------------------------------- */ @@ -515,8 +515,7 @@ static void xlog_outrec(StringInfo buf, XLogRecord *record); #endif static void issue_xlog_fsync(void); static void pg_start_backup_callback(int code, Datum arg); -static bool read_backup_label(XLogRecPtr *checkPointLoc, - XLogRecPtr *minRecoveryLoc); +static bool read_backup_label(XLogRecPtr *checkPointLoc); static void rm_redo_error_callback(void *arg); static int get_sync_bit(int method); @@ -5355,7 +5354,6 @@ StartupXLOG(void) bool haveBackupLabel = false; XLogRecPtr RecPtr, checkPointLoc, - backupStopLoc, EndOfLog; uint32 endLogId; uint32 endLogSeg; @@ -5454,7 +5452,7 @@ StartupXLOG(void) recoveryTargetTLI, ControlFile->checkPointCopy.ThisTimeLineID))); - if (read_backup_label(&checkPointLoc, &backupStopLoc)) + if (read_backup_label(&checkPointLoc)) { /* * When a backup_label file is present, we want to roll forward from @@ -5597,11 +5595,23 @@ StartupXLOG(void) ControlFile->prevCheckPoint = ControlFile->checkPoint; ControlFile->checkPoint = checkPointLoc; ControlFile->checkPointCopy = checkPoint; - if (backupStopLoc.xlogid != 0 || backupStopLoc.xrecoff != 0) + if (InArchiveRecovery) + { + /* initialize minRecoveryPoint if not set yet */ + if (XLByteLT(ControlFile->minRecoveryPoint, checkPoint.redo)) + ControlFile->minRecoveryPoint = checkPoint.redo; + } + else { - if (XLByteLT(ControlFile->minRecoveryPoint, backupStopLoc)) - ControlFile->minRecoveryPoint = backupStopLoc; + XLogRecPtr InvalidXLogRecPtr = {0, 0}; + ControlFile->minRecoveryPoint = InvalidXLogRecPtr; } + /* + * set backupStartupPoint if we're starting archive recovery from a + * base backup + */ + if (haveBackupLabel) + ControlFile->backupStartPoint = checkPoint.redo; ControlFile->time = (pg_time_t) time(NULL); /* No need to hold ControlFileLock yet, we aren't up far enough */ UpdateControlFile(); @@ -5703,15 +5713,9 @@ StartupXLOG(void) InRedo = true; - if (minRecoveryPoint.xlogid == 0 && minRecoveryPoint.xrecoff == 0) - ereport(LOG, - (errmsg("redo starts at %X/%X", - ReadRecPtr.xlogid, ReadRecPtr.xrecoff))); - else - ereport(LOG, - (errmsg("redo starts at %X/%X, consistency will be reached at %X/%X", - ReadRecPtr.xlogid, ReadRecPtr.xrecoff, - minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff))); + ereport(LOG, + (errmsg("redo starts at %X/%X", + ReadRecPtr.xlogid, ReadRecPtr.xrecoff))); /* * Let postmaster know we've started redo now, so that it can @@ -5771,7 +5775,8 @@ StartupXLOG(void) * Have we passed our safe starting point? */ if (!reachedMinRecoveryPoint && - XLByteLE(minRecoveryPoint, EndRecPtr)) + XLByteLE(minRecoveryPoint, EndRecPtr) && + XLogRecPtrIsInvalid(ControlFile->backupStartPoint)) { reachedMinRecoveryPoint = true; ereport(LOG, @@ -5877,7 +5882,9 @@ StartupXLOG(void) * be further ahead --- ControlFile->minRecoveryPoint cannot have been * advanced beyond the WAL we processed. */ - if (InRecovery && XLByteLT(EndOfLog, minRecoveryPoint)) + if (InArchiveRecovery && + (XLByteLT(EndOfLog, minRecoveryPoint) || + !XLogRecPtrIsInvalid(ControlFile->backupStartPoint))) { if (reachedStopPoint) /* stopped because of stop request */ ereport(FATAL, @@ -7312,6 +7319,32 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record) { /* nothing to do here */ } + else if (info == XLOG_BACKUP_END) + { + XLogRecPtr startpoint; + memcpy(&startpoint, XLogRecGetData(record), sizeof(startpoint)); + + if (XLByteEQ(ControlFile->backupStartPoint, startpoint)) + { + /* + * We have reached the end of base backup, the point where + * pg_stop_backup() was done. The data on disk is now consistent. + * Reset backupStartPoint, and update minRecoveryPoint to make + * sure we don't allow starting up at an earlier point even if + * recovery is stopped and restarted soon after this. + */ + elog(DEBUG1, "end of backup reached"); + + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); + + if (XLByteLT(ControlFile->minRecoveryPoint, lsn)) + ControlFile->minRecoveryPoint = lsn; + MemSet(&ControlFile->backupStartPoint, 0, sizeof(XLogRecPtr)); + UpdateControlFile(); + + LWLockRelease(ControlFileLock); + } + } } void @@ -7353,6 +7386,14 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec) { appendStringInfo(buf, "xlog switch"); } + else if (info == XLOG_BACKUP_END) + { + XLogRecPtr startpoint; + + memcpy(&startpoint, rec, sizeof(XLogRecPtr)); + appendStringInfo(buf, "backup end: %X/%X", + startpoint.xlogid, startpoint.xrecoff); + } else appendStringInfo(buf, "UNKNOWN"); } @@ -7688,10 +7729,14 @@ pg_start_backup_callback(int code, Datum arg) /* * pg_stop_backup: finish taking an on-line backup dump * - * We remove the backup label file created by pg_start_backup, and instead - * create a backup history file in pg_xlog (whence it will immediately be - * archived). The backup history file contains the same info found in - * the label file, plus the backup-end time and WAL location. + * We write an end-of-backup WAL record, and remove the backup label file + * created by pg_start_backup, creating a backup history file in pg_xlog + * instead (whence it will immediately be archived). The backup history file + * contains the same info found in the label file, plus the backup-end time + * and WAL location. Before 8.5, the backup-end time was read from the backup + * history file at the beginning of archive recovery, but we now use the WAL + * record for that and the file is for informational and debug purposes only. + * * Note: different from CancelBackup which just cancels online backup mode. */ Datum @@ -7699,6 +7744,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) { XLogRecPtr startpoint; XLogRecPtr stoppoint; + XLogRecData rdata; pg_time_t stamp_time; char strfbuf[128]; char histfilepath[MAXPGPATH]; @@ -7739,22 +7785,6 @@ pg_stop_backup(PG_FUNCTION_ARGS) XLogCtl->Insert.forcePageWrites = false; LWLockRelease(WALInsertLock); - /* - * Force a switch to a new xlog segment file, so that the backup is valid - * as soon as archiver moves out the current segment file. We'll report - * the end address of the XLOG SWITCH record as the backup stopping point. - */ - stoppoint = RequestXLogSwitch(); - - XLByteToSeg(stoppoint, _logId, _logSeg); - XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg); - - /* Use the log timezone here, not the session timezone */ - stamp_time = (pg_time_t) time(NULL); - pg_strftime(strfbuf, sizeof(strfbuf), - "%Y-%m-%d %H:%M:%S %Z", - pg_localtime(&stamp_time, log_timezone)); - /* * Open the existing label file */ @@ -7782,6 +7812,30 @@ pg_stop_backup(PG_FUNCTION_ARGS) (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE))); + /* + * Write the backup-end xlog record + */ + rdata.data = (char *) (&startpoint); + rdata.len = sizeof(startpoint); + rdata.buffer = InvalidBuffer; + rdata.next = NULL; + stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END, &rdata); + + /* + * Force a switch to a new xlog segment file, so that the backup is valid + * as soon as archiver moves out the current segment file. + */ + RequestXLogSwitch(); + + XLByteToSeg(stoppoint, _logId, _logSeg); + XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg); + + /* Use the log timezone here, not the session timezone */ + stamp_time = (pg_time_t) time(NULL); + pg_strftime(strfbuf, sizeof(strfbuf), + "%Y-%m-%d %H:%M:%S %Z", + pg_localtime(&stamp_time, log_timezone)); + /* * Write the backup history file */ @@ -8088,33 +8142,18 @@ pg_xlogfile_name(PG_FUNCTION_ARGS) * later than the start of the dump, and so if we rely on it as the start * point, we will fail to restore a consistent database state. * - * We also attempt to retrieve the corresponding backup history file. - * If successful, set *minRecoveryLoc to constrain valid PITR stopping - * points. - * * Returns TRUE if a backup_label was found (and fills the checkpoint * location into *checkPointLoc); returns FALSE if not. */ static bool -read_backup_label(XLogRecPtr *checkPointLoc, XLogRecPtr *minRecoveryLoc) +read_backup_label(XLogRecPtr *checkPointLoc) { XLogRecPtr startpoint; - XLogRecPtr stoppoint; - char histfilename[MAXFNAMELEN]; - char histfilepath[MAXPGPATH]; char startxlogfilename[MAXFNAMELEN]; - char stopxlogfilename[MAXFNAMELEN]; TimeLineID tli; - uint32 _logId; - uint32 _logSeg; FILE *lfp; - FILE *fp; char ch; - /* Default is to not constrain recovery stop point */ - minRecoveryLoc->xlogid = 0; - minRecoveryLoc->xrecoff = 0; - /* * See if label file is present */ @@ -8152,45 +8191,6 @@ read_backup_label(XLogRecPtr *checkPointLoc, XLogRecPtr *minRecoveryLoc) errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE))); - /* - * Try to retrieve the backup history file (no error if we can't) - */ - XLByteToSeg(startpoint, _logId, _logSeg); - BackupHistoryFileName(histfilename, tli, _logId, _logSeg, - startpoint.xrecoff % XLogSegSize); - - if (InArchiveRecovery) - RestoreArchivedFile(histfilepath, histfilename, "RECOVERYHISTORY", 0); - else - BackupHistoryFilePath(histfilepath, tli, _logId, _logSeg, - startpoint.xrecoff % XLogSegSize); - - fp = AllocateFile(histfilepath, "r"); - if (fp) - { - /* - * Parse history file to identify stop point. - */ - if (fscanf(fp, "START WAL LOCATION: %X/%X (file %24s)%c", - &startpoint.xlogid, &startpoint.xrecoff, startxlogfilename, - &ch) != 4 || ch != '\n') - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("invalid data in file \"%s\"", histfilename))); - if (fscanf(fp, "STOP WAL LOCATION: %X/%X (file %24s)%c", - &stoppoint.xlogid, &stoppoint.xrecoff, stopxlogfilename, - &ch) != 4 || ch != '\n') - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("invalid data in file \"%s\"", histfilename))); - *minRecoveryLoc = stoppoint; - if (ferror(fp) || FreeFile(fp)) - ereport(FATAL, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", - histfilepath))); - } - return true; } diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 9551a1d8786e6efa9ad9fc97dd96ef5dd8700e31..2735a6611e2755a9b65317ff23e2925a5cdaca68 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -6,7 +6,7 @@ * copyright (c) Oliver Elphick <olly@lfix.co.uk>, 2001; * licence: BSD * - * $PostgreSQL: pgsql/src/bin/pg_controldata/pg_controldata.c,v 1.45 2009/12/19 01:32:38 sriggs Exp $ + * $PostgreSQL: pgsql/src/bin/pg_controldata/pg_controldata.c,v 1.46 2010/01/04 12:50:49 heikki Exp $ */ #include "postgres_fe.h" @@ -196,13 +196,16 @@ main(int argc, char *argv[]) ControlFile.checkPointCopy.oldestXid); printf(_("Latest checkpoint's oldestXID's DB: %u\n"), ControlFile.checkPointCopy.oldestXidDB); - printf(_("Latest checkpoint's oldestActiveXID: %u\n"), + printf(_("Latest checkpoint's oldestActiveXID: %u\n"), ControlFile.checkPointCopy.oldestActiveXid); printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); printf(_("Minimum recovery ending location: %X/%X\n"), ControlFile.minRecoveryPoint.xlogid, ControlFile.minRecoveryPoint.xrecoff); + printf(_("Backup start location: %X/%X\n"), + ControlFile.backupStartPoint.xlogid, + ControlFile.backupStartPoint.xrecoff); printf(_("Maximum data alignment: %u\n"), ControlFile.maxAlign); /* we don't print floatFormat since can't say much useful about it */ diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index 8e0ba5073dd21e160ff94714852b0fada0e50700..07ea5fd13bcc0254a6fbef2bf9f4db085ab72dbb 100644 --- a/src/bin/pg_resetxlog/pg_resetxlog.c +++ b/src/bin/pg_resetxlog/pg_resetxlog.c @@ -23,7 +23,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.76 2010/01/02 16:57:59 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.77 2010/01/04 12:50:49 heikki Exp $ * *------------------------------------------------------------------------- */ @@ -625,6 +625,8 @@ RewriteControlFile(void) ControlFile.prevCheckPoint.xrecoff = 0; ControlFile.minRecoveryPoint.xlogid = 0; ControlFile.minRecoveryPoint.xrecoff = 0; + ControlFile.backupStartPoint.xlogid = 0; + ControlFile.backupStartPoint.xrecoff = 0; /* Now we can force the recorded xlog seg size to the right thing. */ ControlFile.xlog_seg_size = XLogSegSize; diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index aed93501a8a6157a9c0e55b8e3d6c48ad20c6e99..49bb91981834362f6ea2665b41513b3cd076f525 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.565 2010/01/02 16:58:01 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.566 2010/01/04 12:50:49 heikki Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201001011 +#define CATALOG_VERSION_NO 201001041 #endif diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 454fc23a3064af04a8d3d4b78eaa72a793f2c4f7..bac3e2a7ee9157ad674262172445a223cd60b234 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.47 2010/01/02 16:58:01 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.48 2010/01/04 12:50:50 heikki Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 852 +#define PG_CONTROL_VERSION 853 /* * Body of CheckPoint XLOG records. This is declared here because we keep @@ -62,6 +62,7 @@ typedef struct CheckPoint #define XLOG_NOOP 0x20 #define XLOG_NEXTOID 0x30 #define XLOG_SWITCH 0x40 +#define XLOG_BACKUP_END 0x50 /* System status indicator */ @@ -117,7 +118,27 @@ typedef struct ControlFileData CheckPoint checkPointCopy; /* copy of last check point record */ - XLogRecPtr minRecoveryPoint; /* must replay xlog to here */ + /* + * These two values determine the minimum point we must recover up to + * before starting up: + * + * minRecoveryPoint is updated to the latest replayed LSN whenever we + * flush a data change during archive recovery. That guards against + * starting archive recovery, aborting it, and restarting with an earlier + * stop location. If we've already flushed data changes from WAL record X + * to disk, we mustn't start up until we reach X again. Zero when not + * doing archive recovery. + * + * backupStartPoint is the redo pointer of the backup start checkpoint, if + * we are recovering from an online backup and haven't reached the end of + * backup yet. It is reset to zero when the end of backup is reached, and + * we mustn't start up before that. A boolean would suffice otherwise, but + * we use the redo pointer as a cross-check when we see an end-of-backup + * record, to make sure the end-of-backup record corresponds the base + * backup we're recovering from. + */ + XLogRecPtr minRecoveryPoint; + XLogRecPtr backupStartPoint; /* * This data is used to check for hardware-architecture compatibility of