On Tue, Sep 27, 2011 at 11:56 AM, Fujii Masao <masao.fu...@gmail.com> wrote: >> In backup.sgml the new section titled "Making a Base Backup during >> Recovery" I would prefer to see some mention in the title that this >> procedure is for standby servers ie "Making a Base Backup from a Standby >> Database". Users who have setup a hot-standby database should be familiar >> with the 'standby' terminology. I agree that the "during recovery" >> description is technically correct but I'm not sure someone who is looking >> through the manual for instructions on making a base backup from here >> standby will realize this is the section they should read. > > I used the term "recovery" rather than "standby" because we can take > a backup even from the server in normal archive recovery mode but not > standby mode. But there is not many users who take a backup during > normal archive recovery, so I agree that the term "standby" is better to > be used in the document. Will change.
Done. >> Around line 969 where you give an example of copying the control file I >> would be a bit clearer that this is an example command. Ie (Copy the >> pg_control file from the cluster directory to the global sub-directory of >> the backup. For example "cp $PGDATA/global/pg_control >> /mnt/server/backupdir/global") > > Looks better. Will change. Done. >> or give an error message on >> pg_stop_backup() saying that the base backup won't be usable. The above >> error doesn't really tell the user why there is a mismatch. > > What about the following error message? > > ERROR: pg_stop_backup() was executed during normal processing though > pg_start_backup() was executed during recovery > HINT: The database backup will not be usable. Done. I attached the new version of the patch. Regards, -- Fujii Masao NIPPON TELEGRAPH AND TELEPHONE CORPORATION NTT Open Source Software Center
*** a/doc/src/sgml/backup.sgml --- b/doc/src/sgml/backup.sgml *************** *** 935,940 **** SELECT pg_stop_backup(); --- 935,1000 ---- </para> </sect2> + <sect2 id="backup-from-standby"> + <title>Making a Base Backup from Standby Database</title> + + <para> + It's possible to make a base backup during recovery. Which allows a user + to take a base backup from the standby to offload the expense of + periodic backups from the master. Its procedure is similar to that + during normal running. All these steps must be performed on the standby. + <orderedlist> + <listitem> + <para> + Ensure that hot standby is enabled (see <xref linkend="hot-standby"> + for more information). + </para> + </listitem> + <listitem> + <para> + Connect to the database as a superuser and execute <function>pg_start_backup</>. + This performs a restartpoint if there is at least one checkpoint record + replayed since last restartpoint. + </para> + </listitem> + <listitem> + <para> + Perform a file system backup. + </para> + </listitem> + <listitem> + <para> + Copy the pg_control file from the cluster directory to the global + sub-directory of the backup. For example: + <programlisting> + cp $PGDATA/global/pg_control /mnt/server/backupdir/global + </programlisting> + </para> + </listitem> + <listitem> + <para> + Again connect to the database as a superuser, and execute + <function>pg_stop_backup</>. This terminates the backup mode, but does not + perform a switch to the next WAL segment, create a backup history file and + wait for all required WAL segments to be archived, + unlike that during normal processing. + </para> + </listitem> + </orderedlist> + </para> + + <para> + You cannot use the <application>pg_basebackup</> tool to take the backup + from the standby. + </para> + <para> + It's not possible to make a base backup from the server in recovery mode + when reading WAL written during a period when <varname>full_page_writes</> + was disabled. If you want to take a base backup from the standby, + <varname>full_page_writes</> must be set to true on the master. + </para> + </sect2> + <sect2 id="backup-pitr-recovery"> <title>Recovering Using a Continuous Archive Backup</title> *** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** *** 1680,1685 **** SET ENABLE_SEQSCAN TO OFF; --- 1680,1693 ---- </para> <para> + WAL written while <varname>full_page_writes</> is disabled does not + contain enough information to make a base backup during recovery + (see <xref linkend="backup-from-standby">), + so <varname>full_page_writes</> must be enabled on the master + to take a backup from the standby. + </para> + + <para> This parameter can only be set in the <filename>postgresql.conf</> file or on the server command line. The default is <literal>on</>. *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 14014,14020 **** SELECT set_config('log_statement_stats', 'off', false); <para> The functions shown in <xref linkend="functions-admin-backup-table"> assist in making on-line backups. ! These functions cannot be executed during recovery. </para> <table id="functions-admin-backup-table"> --- 14014,14021 ---- <para> The functions shown in <xref linkend="functions-admin-backup-table"> assist in making on-line backups. ! These functions except <function>pg_start_backup</> and <function>pg_stop_backup</> ! cannot be executed during recovery. </para> <table id="functions-admin-backup-table"> *************** *** 14094,14100 **** SELECT set_config('log_statement_stats', 'off', false); database cluster's data directory, performs a checkpoint, and then returns the backup's starting transaction log location as text. The user can ignore this result value, but it is ! provided in case it is useful. <programlisting> postgres=# select pg_start_backup('label_goes_here'); pg_start_backup --- 14095,14103 ---- database cluster's data directory, performs a checkpoint, and then returns the backup's starting transaction log location as text. The user can ignore this result value, but it is ! provided in case it is useful. If <function>pg_start_backup</> is ! executed during recovery, it performs a restartpoint rather than ! writing a new checkpoint. <programlisting> postgres=# select pg_start_backup('label_goes_here'); pg_start_backup *************** *** 14122,14127 **** postgres=# select pg_start_backup('label_goes_here'); --- 14125,14137 ---- </para> <para> + If <function>pg_stop_backup</> is executed during recovery, it just + removes the label file, but doesn't create a backup history file and wait for + the ending transaction log file to be archived. The return value is equal to + or bigger than the exact backup's ending transaction log location. + </para> + + <para> <function>pg_switch_xlog</> moves to the next transaction log file, allowing the current file to be archived (assuming you are using continuous archiving). The return value is the ending transaction log location + 1 within the just-completed transaction log file. *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** *** 664,670 **** static void xlog_outrec(StringInfo buf, XLogRecord *record); #endif static void pg_start_backup_callback(int code, Datum arg); static bool read_backup_label(XLogRecPtr *checkPointLoc, ! bool *backupEndRequired); static void rm_redo_error_callback(void *arg); static int get_sync_bit(int method); --- 664,670 ---- #endif static void pg_start_backup_callback(int code, Datum arg); static bool read_backup_label(XLogRecPtr *checkPointLoc, ! bool *backupEndRequired, bool *backupDuringRecovery); static void rm_redo_error_callback(void *arg); static int get_sync_bit(int method); *************** *** 6023,6028 **** StartupXLOG(void) --- 6023,6030 ---- uint32 freespace; TransactionId oldestActiveXID; bool backupEndRequired = false; + bool backupDuringRecovery = false; + DBState save_state; /* * Read control file and check XLOG status looks valid. *************** *** 6156,6162 **** StartupXLOG(void) if (StandbyMode) OwnLatch(&XLogCtl->recoveryWakeupLatch); ! if (read_backup_label(&checkPointLoc, &backupEndRequired)) { /* * When a backup_label file is present, we want to roll forward from --- 6158,6165 ---- if (StandbyMode) OwnLatch(&XLogCtl->recoveryWakeupLatch); ! if (read_backup_label(&checkPointLoc, &backupEndRequired, ! &backupDuringRecovery)) { /* * When a backup_label file is present, we want to roll forward from *************** *** 6312,6317 **** StartupXLOG(void) --- 6315,6321 ---- * pg_control with any minimum recovery stop point obtained from a * backup history file. */ + save_state = ControlFile->state; if (InArchiveRecovery) ControlFile->state = DB_IN_ARCHIVE_RECOVERY; else *************** *** 6332,6343 **** StartupXLOG(void) } /* ! * set backupStartPoint if we're starting recovery from a base backup */ if (haveBackupLabel) { ControlFile->backupStartPoint = checkPoint.redo; ControlFile->backupEndRequired = backupEndRequired; } ControlFile->time = (pg_time_t) time(NULL); /* No need to hold ControlFileLock yet, we aren't up far enough */ --- 6336,6369 ---- } /* ! * Set backupStartPoint if we're starting recovery from a base backup. ! * ! * Set backupEndPoint if we're starting recovery from a base backup ! * which was taken from the server in recovery mode. We confirm ! * that minRecoveryPoint can be used as the backup end location by ! * checking whether the database system status in pg_control indicates ! * DB_IN_ARCHIVE_RECOVERY. If minRecoveryPoint is not available, ! * there is no way to know the backup end location, so we cannot ! * advance recovery any more. In this case, we have to cancel recovery ! * before changing the database system status in pg_control to ! * DB_IN_ARCHIVE_RECOVERY because otherwise subsequent ! * restarted recovery would go through this check wrongly. */ if (haveBackupLabel) { ControlFile->backupStartPoint = checkPoint.redo; ControlFile->backupEndRequired = backupEndRequired; + + if (backupDuringRecovery) + { + if (save_state != DB_IN_ARCHIVE_RECOVERY) + ereport(FATAL, + (errmsg("database system status mismatches between " + "pg_control and backup_label"), + errhint("This means that the backup is corrupted and you will " + "have to use another backup for recovery."))); + ControlFile->backupEndPoint = ControlFile->minRecoveryPoint; + } } ControlFile->time = (pg_time_t) time(NULL); /* No need to hold ControlFileLock yet, we aren't up far enough */ *************** *** 6617,6622 **** StartupXLOG(void) --- 6643,6670 ---- /* Pop the error context stack */ error_context_stack = errcontext.previous; + if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) && + XLByteLE(ControlFile->backupEndPoint, EndRecPtr)) + { + /* + * We have reached the end of base backup, the point where + * the minimum recovery point in pg_control which was + * backed up just before pg_stop_backup() indicates. + * The data on disk is now consistent. Reset backupStartPoint + * and backupEndPoint. + */ + elog(DEBUG1, "end of backup reached"); + + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); + + MemSet(&ControlFile->backupStartPoint, 0, sizeof(XLogRecPtr)); + MemSet(&ControlFile->backupEndPoint, 0, sizeof(XLogRecPtr)); + ControlFile->backupEndRequired = false; + UpdateControlFile(); + + LWLockRelease(ControlFileLock); + } + /* * Update shared recoveryLastRecPtr after this record has been * replayed. *************** *** 8417,8423 **** xlog_redo(XLogRecPtr lsn, XLogRecord *record) * never arrive. */ if (InArchiveRecovery && ! !XLogRecPtrIsInvalid(ControlFile->backupStartPoint)) ereport(ERROR, (errmsg("online backup was canceled, recovery cannot continue"))); --- 8465,8472 ---- * never arrive. */ if (InArchiveRecovery && ! !XLogRecPtrIsInvalid(ControlFile->backupStartPoint) && ! XLogRecPtrIsInvalid(ControlFile->backupEndPoint)) ereport(ERROR, (errmsg("online backup was canceled, recovery cannot continue"))); *************** *** 8880,8885 **** XLogRecPtr --- 8929,8935 ---- do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) { bool exclusive = (labelfile == NULL); + bool recovery_in_progress = false; XLogRecPtr checkpointloc; XLogRecPtr startpoint; pg_time_t stamp_time; *************** *** 8891,8908 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) FILE *fp; StringInfoData labelfbuf; if (!superuser() && !is_authenticated_user_replication_role()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); ! if (RecoveryInProgress()) ! ereport(ERROR, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("recovery is in progress"), ! errhint("WAL control functions cannot be executed during recovery."))); ! ! if (!XLogIsNeeded()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("WAL level not sufficient for making an online backup"), --- 8941,8960 ---- FILE *fp; StringInfoData labelfbuf; + recovery_in_progress = RecoveryInProgress(); + if (!superuser() && !is_authenticated_user_replication_role()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); ! /* ! * During recovery, we don't need to check WAL level. Because the fact that ! * we are now executing pg_start_backup() during recovery means that ! * wal_level is set to hot_standby on the master, i.e., WAL level is sufficient ! * for making an online backup. ! */ ! if (!recovery_in_progress && !XLogIsNeeded()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("WAL level not sufficient for making an online backup"), *************** *** 8924,8931 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) * 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 --- 8976,8988 ---- * 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. + * + * During recovery, we skip forcing XLOG file switch, which means that + * the backup taken during recovery is not available for the special recovery + * case described above. */ ! if (!recovery_in_progress) ! RequestXLogSwitch(); /* * Mark backup active in shared memory. We must do full-page WAL writes *************** *** 8941,8946 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) --- 8998,9006 ---- * since we expect that any pages not modified during the backup interval * must have been correctly captured by the backup.) * + * Note that forcePageWrites has no effect during an online backup from + * the server in recovery mode. + * * We must hold WALInsertLock to change the value of forcePageWrites, to * ensure adequate interlocking against XLogInsert(). */ *************** *** 8970,8980 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) 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. */ --- 9030,9048 ---- 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. * + * During recovery, establish a restartpoint if possible. We use the last + * restartpoint as the backup starting checkpoint. This means that two + * successive backup runs can have same checkpoint positions. + * + * Since the fact that we are executing pg_start_backup() during + * recovery means that bgwriter is running, we can use + * RequestCheckpoint() to establish a restartpoint. + * * We use CHECKPOINT_IMMEDIATE only if requested by user (via * passing fast = true). Otherwise this can take awhile. */ *************** *** 9002,9007 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) --- 9070,9081 ---- * 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. + * + * During recovery, since we don't use the end-of-backup WAL + * record and don't write the backup history file, the starting WAL + * location doesn't need to be unique. This means that two base + * backups started at the same time might use the same checkpoint + * as starting locations. */ LWLockAcquire(WALInsertLock, LW_SHARED); if (XLByteLT(XLogCtl->Insert.lastBackupStart, startpoint)) *************** *** 9010,9015 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) --- 9084,9091 ---- gotUniqueStartpoint = true; } LWLockRelease(WALInsertLock); + if (recovery_in_progress) + gotUniqueStartpoint = true; } while (!gotUniqueStartpoint); XLByteToSeg(startpoint, _logId, _logSeg); *************** *** 9031,9036 **** do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile) --- 9107,9114 ---- checkpointloc.xlogid, checkpointloc.xrecoff); appendStringInfo(&labelfbuf, "BACKUP METHOD: %s\n", exclusive ? "pg_start_backup" : "streamed"); + appendStringInfo(&labelfbuf, "SYSTEM STATUS: %s\n", + recovery_in_progress ? "recovery" : "in production"); appendStringInfo(&labelfbuf, "START TIME: %s\n", strfbuf); appendStringInfo(&labelfbuf, "LABEL: %s\n", backupidstr); *************** *** 9123,9128 **** pg_start_backup_callback(int code, Datum arg) --- 9201,9208 ---- * 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. * + * During recovery, we only remove the backup label file. + * * Note: different from CancelBackup which just cancels online backup mode. */ Datum *************** *** 9149,9154 **** XLogRecPtr --- 9229,9235 ---- do_pg_stop_backup(char *labelfile, bool waitforarchive) { bool exclusive = (labelfile == NULL); + bool recovery_in_progress = false; XLogRecPtr startpoint; XLogRecPtr stoppoint; XLogRecData rdata; *************** *** 9159,9164 **** do_pg_stop_backup(char *labelfile, bool waitforarchive) --- 9240,9246 ---- char stopxlogfilename[MAXFNAMELEN]; char lastxlogfilename[MAXFNAMELEN]; char histfilename[MAXFNAMELEN]; + char systemstatus[20]; uint32 _logId; uint32 _logSeg; FILE *lfp; *************** *** 9168,9186 **** do_pg_stop_backup(char *labelfile, bool waitforarchive) int waits = 0; bool reported_waiting = false; char *remaining; if (!superuser() && !is_authenticated_user_replication_role()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); ! if (RecoveryInProgress()) ! ereport(ERROR, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("recovery is in progress"), ! errhint("WAL control functions cannot be executed during recovery."))); ! ! if (!XLogIsNeeded()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("WAL level not sufficient for making an online backup"), --- 9250,9271 ---- int waits = 0; bool reported_waiting = false; char *remaining; + char *ptr; + + recovery_in_progress = RecoveryInProgress(); if (!superuser() && !is_authenticated_user_replication_role()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); ! /* ! * During recovery, we don't need to check WAL level. Because the fact that ! * we are now executing pg_stop_backup() means that wal_level is set to ! * hot_standby on the master, i.e., WAL level is sufficient for making an online ! * backup. ! */ ! if (!recovery_in_progress && !XLogIsNeeded()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("WAL level not sufficient for making an online backup"), *************** *** 9271,9276 **** do_pg_stop_backup(char *labelfile, bool waitforarchive) --- 9356,9414 ---- remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */ /* + * Parse the SYSTEM STATUS line, and check that database system + * status matches between pg_start_backup() and pg_stop_backup(). + */ + ptr = strstr(remaining, "SYSTEM STATUS:"); + if (sscanf(ptr, "SYSTEM STATUS: %19s\n", systemstatus) != 1) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE))); + if (strcmp(systemstatus, "recovery") == 0 && !recovery_in_progress) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("pg_stop_backup() was executed during normal processing " + "though pg_start_backup() was executed during recovery"), + errhint("The database backup will not be usable."))); + + /* + * During recovery, we don't write an end-of-backup record. We can + * assume that pg_control was backed up just before pg_stop_backup() + * and its minimum recovery point can be available as the backup end + * location. Without an end-of-backup record, we can check correctly + * whether we've reached the end of backup when starting recovery + * from this backup. + * + * We don't force a switch to new WAL file and wait for all the required + * files to be archived. This is okay if we use the backup to start + * the standby. But, if it's for an archive recovery, to ensure all the + * required files are available, a user should wait for them to be archived, + * or include them into the backup after pg_stop_backup(). + * + * We return the current minimum recovery point as the backup end + * location. Note that it's would be bigger than the exact backup end + * location if the minimum recovery point is updated since the backup + * of pg_control. The return value of pg_stop_backup() is often used + * for a user to calculate the required files. Returning approximate + * location is harmless for that use because it's guaranteed not to be + * smaller than the exact backup end location. + * + * XXX currently a backup history file is for informational and debug + * purposes only. It's not essential for an online backup. Furthermore, + * even if it's created, it will not be archived during recovery because + * an archiver is not invoked. So it doesn't seem worthwhile to write + * a backup history file during recovery. + */ + if (recovery_in_progress) + { + LWLockAcquire(ControlFileLock, LW_SHARED); + stoppoint = ControlFile->minRecoveryPoint; + LWLockRelease(ControlFileLock); + + return stoppoint; + } + + /* * Write the backup-end xlog record */ rdata.data = (char *) (&startpoint); *************** *** 9787,9804 **** pg_xlogfile_name(PG_FUNCTION_ARGS) * Returns TRUE if a backup_label was found (and fills the checkpoint * location and its REDO location into *checkPointLoc and RedoStartLSN, * respectively); returns FALSE if not. If this backup_label came from a ! * streamed backup, *backupEndRequired is set to TRUE. */ static bool ! read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired) { char startxlogfilename[MAXFNAMELEN]; TimeLineID tli; FILE *lfp; char ch; char backuptype[20]; *backupEndRequired = false; /* * See if label file is present --- 9925,9946 ---- * Returns TRUE if a backup_label was found (and fills the checkpoint * location and its REDO location into *checkPointLoc and RedoStartLSN, * respectively); returns FALSE if not. If this backup_label came from a ! * streamed backup, *backupEndRequired is set to TRUE. If this backup_label ! * was created during recovery, *backupDuringRecovery is set to TRUE. */ static bool ! read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired, ! bool *backupDuringRecovery) { char startxlogfilename[MAXFNAMELEN]; TimeLineID tli; FILE *lfp; char ch; char backuptype[20]; + char systemstatus[20]; *backupEndRequired = false; + *backupDuringRecovery = false; /* * See if label file is present *************** *** 9832,9847 **** read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired) (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE))); /* ! * BACKUP METHOD line is new in 9.1. We can't restore from an older backup ! * anyway, but since the information on it is not strictly required, don't ! * error out if it's missing for some reason. */ ! if (fscanf(lfp, "BACKUP METHOD: %19s", backuptype) == 1) { if (strcmp(backuptype, "streamed") == 0) *backupEndRequired = true; } if (ferror(lfp) || FreeFile(lfp)) ereport(FATAL, (errcode_for_file_access(), --- 9974,9995 ---- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE))); /* ! * BACKUP METHOD and SYSTEM STATUS lines are new in 9.2. We can't ! * restore from an older backup anyway, but since the information on it ! * is not strictly required, don't error out if it's missing for some reason. */ ! if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1) { if (strcmp(backuptype, "streamed") == 0) *backupEndRequired = true; } + if (fscanf(lfp, "SYSTEM STATUS: %19s\n", systemstatus) == 1) + { + if (strcmp(systemstatus, "recovery") == 0) + *backupDuringRecovery = true; + } + if (ferror(lfp) || FreeFile(lfp)) ereport(FATAL, (errcode_for_file_access(), *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** *** 287,292 **** typedef enum --- 287,294 ---- static PMState pmState = PM_INIT; static bool ReachedNormalRunning = false; /* T if we've reached PM_RUN */ + static bool OnlineBackupAllowed = false; /* T if we've reached PM_RUN or + * PM_HOT_STANDBY */ bool ClientAuthInProgress = false; /* T during new-client * authentication */ *************** *** 2105,2122 **** pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); ! ! /* ! * If we're in recovery, we can't kill the startup process ! * right away, because at present doing so does not release ! * its locks. We might want to change this in a future ! * release. For the time being, the PM_WAIT_READONLY state ! * indicates that we're waiting for the regular (read only) ! * backends to die off; once they do, we'll kill the startup ! * and walreceiver processes. ! */ ! pmState = (pmState == PM_RUN) ? ! PM_WAIT_BACKUP : PM_WAIT_READONLY; } /* --- 2107,2113 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); ! pmState = PM_WAIT_BACKUP; } /* *************** *** 2299,2304 **** reaper(SIGNAL_ARGS) --- 2290,2296 ---- */ FatalError = false; ReachedNormalRunning = true; + OnlineBackupAllowed = true; pmState = PM_RUN; /* *************** *** 2823,2831 **** PostmasterStateMachine(void) { /* * PM_WAIT_BACKUP state ends when online backup mode is not active. */ if (!BackupInProgress()) ! pmState = PM_WAIT_BACKENDS; } if (pmState == PM_WAIT_READONLY) --- 2815,2831 ---- { /* * PM_WAIT_BACKUP state ends when online backup mode is not active. + * + * If we're in recovery, we can't kill the startup process right away, + * because at present doing so does not release its locks. We might + * want to change this in a future release. For the time being, + * the PM_WAIT_READONLY state indicates that we're waiting for + * the regular (read only) backends to die off; once they do, + * we'll kill the startup and walreceiver processes. */ if (!BackupInProgress()) ! pmState = ReachedNormalRunning ? ! PM_WAIT_BACKENDS : PM_WAIT_READONLY; } if (pmState == PM_WAIT_READONLY) *************** *** 2994,3006 **** PostmasterStateMachine(void) /* * Terminate backup mode to avoid recovery after a clean fast * shutdown. Since a backup can only be taken during normal ! * running (and not, for example, while running under Hot Standby) ! * it only makes sense to do this if we reached normal running. If ! * we're still in recovery, the backup file is one we're ! * recovering *from*, and we must keep it around so that recovery ! * restarts from the right place. */ ! if (ReachedNormalRunning) CancelBackup(); /* Normal exit from the postmaster is here */ --- 2994,3006 ---- /* * Terminate backup mode to avoid recovery after a clean fast * shutdown. Since a backup can only be taken during normal ! * running and hot standby, it only makes sense to do this ! * if we reached normal running or hot standby. If we have not ! * reached a consistent recovery state yet, the backup file is ! * one we're recovering *from*, and we must keep it around ! * so that recovery restarts from the right place. */ ! if (OnlineBackupAllowed) CancelBackup(); /* Normal exit from the postmaster is here */ *************** *** 4157,4162 **** sigusr1_handler(SIGNAL_ARGS) --- 4157,4163 ---- ereport(LOG, (errmsg("database system is ready to accept read only connections"))); + OnlineBackupAllowed = true; pmState = PM_HOT_STANDBY; } *** a/src/bin/pg_controldata/pg_controldata.c --- b/src/bin/pg_controldata/pg_controldata.c *************** *** 232,237 **** main(int argc, char *argv[]) --- 232,240 ---- printf(_("Backup start location: %X/%X\n"), ControlFile.backupStartPoint.xlogid, ControlFile.backupStartPoint.xrecoff); + printf(_("Backup end location: %X/%X\n"), + ControlFile.backupEndPoint.xlogid, + ControlFile.backupEndPoint.xrecoff); printf(_("End-of-backup record required: %s\n"), ControlFile.backupEndRequired ? _("yes") : _("no")); printf(_("Current wal_level setting: %s\n"), *** a/src/bin/pg_ctl/pg_ctl.c --- b/src/bin/pg_ctl/pg_ctl.c *************** *** 883,897 **** do_stop(void) /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if ! * recovery.conf is also present, we're recovering from an online ! * backup instead of performing one. */ if (shutdown_mode == SMART_MODE && ! stat(backup_file, &statbuf) == 0 && ! stat(recovery_file, &statbuf) != 0) { ! print_msg(_("WARNING: online backup mode is active\n" ! "Shutdown will not complete until pg_stop_backup() is called.\n\n")); } print_msg(_("waiting for server to shut down...")); --- 883,900 ---- /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if ! * recovery.conf is also present and new connection has not been ! * allowed yet, an online backup mode must not be active. */ if (shutdown_mode == SMART_MODE && ! stat(backup_file, &statbuf) == 0) { ! if (stat(recovery_file, &statbuf) != 0) ! print_msg(_("WARNING: online backup mode is active\n" ! "Shutdown will not complete until pg_stop_backup() is called.\n\n")); ! else ! print_msg(_("WARNING: online backup mode is active if you can connect as a superuser to server\n" ! "If so, shutdown will not complete until pg_stop_backup() is called.\n\n")); } print_msg(_("waiting for server to shut down...")); *************** *** 971,985 **** do_restart(void) /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if ! * recovery.conf is also present, we're recovering from an online ! * backup instead of performing one. */ if (shutdown_mode == SMART_MODE && ! stat(backup_file, &statbuf) == 0 && ! stat(recovery_file, &statbuf) != 0) { ! print_msg(_("WARNING: online backup mode is active\n" ! "Shutdown will not complete until pg_stop_backup() is called.\n\n")); } print_msg(_("waiting for server to shut down...")); --- 974,991 ---- /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if ! * recovery.conf is also present and new connection has not been ! * allowed yet, an online backup mode must not be active. */ if (shutdown_mode == SMART_MODE && ! stat(backup_file, &statbuf) == 0) { ! if (stat(recovery_file, &statbuf) != 0) ! print_msg(_("WARNING: online backup mode is active\n" ! "Shutdown will not complete until pg_stop_backup() is called.\n\n")); ! else ! print_msg(_("WARNING: online backup mode is active if you can connect as a superuser to server\n" ! "If so, shutdown will not complete until pg_stop_backup() is called.\n\n")); } print_msg(_("waiting for server to shut down...")); *** a/src/bin/pg_resetxlog/pg_resetxlog.c --- b/src/bin/pg_resetxlog/pg_resetxlog.c *************** *** 503,509 **** GuessControlValues(void) ControlFile.time = (pg_time_t) time(NULL); ControlFile.checkPoint = ControlFile.checkPointCopy.redo; ! /* minRecoveryPoint and backupStartPoint can be left zero */ ControlFile.wal_level = WAL_LEVEL_MINIMAL; ControlFile.MaxConnections = 100; --- 503,509 ---- ControlFile.time = (pg_time_t) time(NULL); ControlFile.checkPoint = ControlFile.checkPointCopy.redo; ! /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */ ControlFile.wal_level = WAL_LEVEL_MINIMAL; ControlFile.MaxConnections = 100; *************** *** 637,642 **** RewriteControlFile(void) --- 637,644 ---- ControlFile.minRecoveryPoint.xrecoff = 0; ControlFile.backupStartPoint.xlogid = 0; ControlFile.backupStartPoint.xrecoff = 0; + ControlFile.backupEndPoint.xlogid = 0; + ControlFile.backupEndPoint.xrecoff = 0; ControlFile.backupEndRequired = false; /* *** a/src/include/catalog/pg_control.h --- b/src/include/catalog/pg_control.h *************** *** 21,27 **** /* Version identifier for this pg_control format */ ! #define PG_CONTROL_VERSION 921 /* * Body of CheckPoint XLOG records. This is declared here because we keep --- 21,27 ---- /* Version identifier for this pg_control format */ ! #define PG_CONTROL_VERSION 922 /* * Body of CheckPoint XLOG records. This is declared here because we keep *************** *** 138,143 **** typedef struct ControlFileData --- 138,150 ---- * record, to make sure the end-of-backup record corresponds the base * backup we're recovering from. * + * backupEndPoint is the backup end location, if we are recovering from + * an online backup which was taken from the server in recovery mode + * and haven't reached the end of backup yet. It is initialized to + * the minimum recovery point in pg_control which was backed up just + * before pg_stop_backup(). It is reset to zero when the end of backup + * is reached, and we mustn't start up before that. + * * If backupEndRequired is true, we know for sure that we're restoring * from a backup, and must see a backup-end record before we can safely * start up. If it's false, but backupStartPoint is set, a backup_label *************** *** 146,151 **** typedef struct ControlFileData --- 153,159 ---- */ XLogRecPtr minRecoveryPoint; XLogRecPtr backupStartPoint; + XLogRecPtr backupEndPoint; bool backupEndRequired; /*
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers