On Tue, 2004-06-29 at 20:59, Simon Riggs wrote: > On Mon, 2004-06-28 at 21:58, Simon Riggs wrote: > > PITR Archive Recovery, 28 June 2004 > > > > What's in this patch?
This my LAST, PLANNED patch before Freeze. Any questions? This is a patch-on-patch, rather than a full patch. To use this, apply earlier patches for pitr_v4_4*, then apply this. (Full patch available upon request...just saving the good people of this list some annoyance time from a 50k download). This now provides: - parsing of restore program from recovery.conf - minor cosmetic changes to some error messages ...there's more to do, but I'm working on the... if it ain't broke, don't fix it... Best Regards, Simon Riggs
Index: xlog.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/access/transam/xlog.c,v retrieving revision 1.146 diff -c -r1.146 xlog.c *** xlog.c 3 Jun 2004 02:08:00 -0000 1.146 --- xlog.c 30 Jun 2004 01:38:52 -0000 *************** *** 35,46 **** #include "storage/proc.h" #include "storage/sinval.h" #include "storage/spin.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/relcache.h" #include "miscadmin.h" - /* * This chunk of hackery attempts to determine which file sync methods * are available on the current platform, and to choose an appropriate --- 35,46 ---- #include "storage/proc.h" #include "storage/sinval.h" #include "storage/spin.h" + #include "storage/pmsignal.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/relcache.h" #include "miscadmin.h" /* * This chunk of hackery attempts to determine which file sync methods * are available on the current platform, and to choose an appropriate *************** *** 84,95 **** /* User-settable parameters */ int CheckPointSegments = 3; int XLOGbuffers = 8; char *XLOG_sync_method = NULL; const char XLOG_sync_method_default[] = DEFAULT_SYNC_METHOD_STR; - char XLOG_archive_dir[MAXPGPATH]; /* null string means - * delete 'em */ #ifdef WAL_DEBUG bool XLOG_DEBUG = false; --- 84,97 ---- /* User-settable parameters */ + bool XLogArchiveMode = false; + bool XLogArchiveDEBUG = false; + char *XLogArchiveDest; + char *XLogArchiveProgram; int CheckPointSegments = 3; int XLOGbuffers = 8; char *XLOG_sync_method = NULL; const char XLOG_sync_method_default[] = DEFAULT_SYNC_METHOD_STR; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; *************** *** 126,131 **** --- 128,141 ---- /* Are we doing recovery by reading XLOG? */ bool InRecovery = false; + bool InArchiveRecovery = false; + bool UseArchiveFirst = false; + bool InRecoveryCleanup = false; + + static char XLogArchRestoreProgram[MAXPGPATH]; + static char recoveryCommandFile[MAXPGPATH]; + + static void readRecoveryCommandFile(void); /* * MyLastRecPtr points to the start of the last XLOG record inserted by the *************** *** 392,397 **** --- 402,408 ---- /* File path names */ static char XLogDir[MAXPGPATH]; + static char RLogDir[MAXPGPATH]; static char ControlFilePath[MAXPGPATH]; /* *************** *** 433,438 **** --- 444,452 ---- static bool InRedo = false; + static bool XLogArchiveNotify(uint32 log, uint32 seg); + static bool XLogArchiveDone(char xlog[MAXPGPATH]); + static void XLogArchiveCleanup(char xlog[32]); static bool AdvanceXLInsertBuffer(void); static bool WasteXLInsertBuffer(void); *************** *** 443,448 **** --- 457,463 ---- bool find_free, int max_advance, bool use_lock); static int XLogFileOpen(uint32 log, uint32 seg, bool econt); + static void RestoreRecoveryXlog(char *path, uint32 log, uint32 seg); static void PreallocXlogFiles(XLogRecPtr endptr); static void MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr); static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, int emode, char *buffer); *************** *** 454,463 **** static void ReadControlFile(void); static char *str_time(time_t tnow); static void issue_xlog_fsync(void); - #ifdef WAL_DEBUG static void xlog_outrec(char *buf, XLogRecord *record); - #endif - /* * Insert an XLOG record having the specified RMID and info bytes, --- 469,475 ---- *************** *** 911,916 **** --- 923,1059 ---- } /* + * XLogArchiveNotify + * + * Writes an archive notification file to the RLogDir + * + * The name of the notification file is the message that will be picked up + * by the archiver, e.g. we write RLogDir/00000001000000C6.ready + * and the archiver then knows to archive XLogDir/00000001000000C6, + * then when complete, rename it to RLogDir/00000001000000C6.done + * + * Called only when in XLogArchiveMode by one backend process + */ + static bool + XLogArchiveNotify(uint32 log, uint32 seg) + { + char rlog[32]; + char rlogpath[MAXPGPATH]; + FILE *rlogFD; + + /* insert an otherwise empty file called <XLOG>.ready */ + sprintf(rlog, "%08X%08X.ready", log, seg); + snprintf(rlogpath, MAXPGPATH, "%s/%s", RLogDir, rlog); + rlogFD = AllocateFile(rlogpath, "w"); + if (rlogFD == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write archive_status file \"%s\" ", + rlogpath))); + FreeFile(rlogFD); + + /* the existence of this file is the message to the archiver to identify + * which files require archiving + * + * if this file is written OK, we then signal the ARCHIVER to do its thang + */ + + if (XLogArchiveDEBUG) + elog(LOG, "backend: written \"%s\"", rlogpath ); + + /* + * don't send the signal if we know that the archiver isn't there (yet) + * - the archiver will see the archive_status file as soon as it starts + */ + if (!InArchiveRecovery) + SendPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER); + + return true; + } + + /* + * XLogArchiveDone + * + * Searches for an archive notification file in RLogDir + * + * Reads RLogDir looking for a specific filename. If that filename ends with .done + * then we know that the filename refers to an xlog in XLogDir that is safe to + * recycle. If the filename ends .ready then thats OK, else we have an error. + * + * Called only when in XLogArchiveMode by bgwriter (when performing checkpoint) + * + * XXX code is rehacked from an earlier version, so needs streamlining + */ + static bool + XLogArchiveDone(char xlog[32]) + { + char rlogpath[MAXPGPATH]; + FILE *rlogFD; + + if (XLogArchiveDEBUG) + elog(LOG, "chkpt: checking for log file \"%s\"", + xlog); + + /* If <XLOG>.done exists then return true + */ + snprintf(rlogpath, MAXPGPATH, "%s/%s.done", RLogDir, xlog); + rlogFD = AllocateFile(rlogpath, "r"); + if (rlogFD != NULL) { + FreeFile(rlogFD); + if (XLogArchiveDEBUG) + elog(LOG, "chkpt: archiving done for log file \"%s\"", + xlog); + return true; + } + else + { + /* + * else if <XLOG>.ready exists then return false and issue WARNING + * ...this indicates archiver is either not working at all or + * if it is, then its just way too slow or incorrectly configured + */ + snprintf(rlogpath, MAXPGPATH, "%s/%s.ready", RLogDir, xlog); + rlogFD = AllocateFile(rlogpath, "r"); + if (rlogFD != NULL) { + FreeFile(rlogFD); + elog(WARNING, "chkpt: archiving not yet started for log file \"%s\"", + xlog); + return false; + } + else + { + /* else issue a WARNING.... a notification file SHOULD exist...unless the + * database has just been restored in which case it may be absent, so + * issue a WARNING, not an error, then return + */ + ereport(WARNING, + (errcode_for_file_access(), + errmsg("chkpt: cannot find archive_status file: %s ", + rlogpath))); + return false; + } + } + } + + /* + * XLogArchiveCleanup + * + * Cleanup an archive notification file for a particular xlog in XLogDir + * + * Called only when in XLogArchiveMode by bgwriter (when performing checkpoint) + * + */ + static void + XLogArchiveCleanup(char xlog[32]) + { + char rlogpath[MAXPGPATH]; + + snprintf(rlogpath, MAXPGPATH, "%s/%s.done", RLogDir, xlog); + unlink(rlogpath); + + } + + /* * Advance the Insert state to the next buffer page, writing out the next * buffer if it still contains unwritten data. * *************** *** 1259,1264 **** --- 1402,1415 ---- { issue_xlog_fsync(); LogwrtResult.Flush = LogwrtResult.Write; /* end of current page */ + + /* + * Notify xlog ready to archive? + */ + if (XLogArchiveMode && !XLogArchiveNotify(openLogId, openLogSeg)) + elog(WARNING, "could not write archive_status file for log %u, segment %u", + openLogId, openLogSeg); + } if (ispartialpage) *************** *** 1685,1691 **** char path[MAXPGPATH]; int fd; ! XLogFileName(path, log, seg); fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT, S_IRUSR | S_IWUSR); --- 1836,1844 ---- char path[MAXPGPATH]; int fd; ! XLogFileName(path, log, seg); ! if (UseArchiveFirst) ! RestoreRecoveryXlog(path, log, seg); fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT, S_IRUSR | S_IWUSR); *************** *** 1704,1714 **** errmsg("could not open file \"%s\" (log file %u, segment %u): %m", path, log, seg))); } ! return (fd); } /* * Preallocate log files beyond the specified log endpoint, according to * the XLOGfile user parameter. */ --- 1857,2047 ---- errmsg("could not open file \"%s\" (log file %u, segment %u): %m", path, log, seg))); } ! return (fd); } /* + * Get next logfile segment to allow recovery + * + */ + static void + RestoreRecoveryXlog(char *path, uint32 log, uint32 seg) + { + char tmpXlog[32]; + char restoreXlog[32]; + char tmppath[MAXPGPATH]; + char xlogRestoreCmd[MAXPGPATH]; + char recoveryXlog[MAXPGPATH]; + char lastrecoXlog[MAXPGPATH]; + int rc; + struct stat stat_buf; + uint32 prevlog, prevseg; + FILE *rlogFD; + + /* + * If a RecoveryFile exists, then we know we are in media recovery + * in which case we choose to recover files from archive, even + * though a file of that name may already exist in XLogDir + * + * By doing this, we do not effect crash recovery code path + * when we are not in archive_mode + * + * We take the archived file because, at the point we took backup, + * the current xlog will most probably be only partially full, + * so we MUST refer to the full version of this file and + * NOT the version of the file that exists with the backup. + * + * We could try to optimize this slightly by checking the local + * copy lastchange timestamp against the archived copy, + * but we have no API to do this, nor can we guarantee that the + * lastchange timestamp was preserved correctly when we copied + * to archive. Our aim is robustness, so we elect not to do this. + * + * Try to copy full xlog from archive to pg_xlog, if it is available + * If that succeeds, we pass the RecoveryXlog filepath back for opening + * If that fails, then we try to read a local file if one exists. + * This allows us to cater for situations where the current xlog + * is still available locally and hasn't yet made it to archive. + * This could happen if: + * - we decide to recover database to undo user data changes + * - we have XLogDir on a different disk and the main DataDir drive + * fails, leaving us with just the XLogDir + * + * Notice that we don't actually overwrite any files when we copy back + * from archive because the XLogArchRestoreProgram may inadvertently + * restore inappropriate xlogs, or they may be corrupt, so we may + * have to fallback to the segments remaining in current XLogDir later. + * The copy-from-archive xlog is always the same, ensuring that we + * don't run out of disk space on long recoveries. + * + * [EMAIL PROTECTED] + */ + + snprintf(recoveryXlog, MAXPGPATH, "%s/RECOVERYXLOG", XLogDir); + snprintf(lastrecoXlog, MAXPGPATH, "%s/LASTRECOXLOG", XLogDir); + + if (stat(recoveryXlog, &stat_buf) == 0) { + /* + * save a copy of the last xlog, before we try to restore + * if the restore fails, we will need it to become current xlog + */ + rc = rename(recoveryXlog, lastrecoXlog); + if (rc !=0) + elog(LOG, "rename failed \"%s\" \"%s\"",recoveryXlog, lastrecoXlog); + /* + * if it fails, ignore it - we'll create one soon... + */ + } + + /* + * Copy xlog from archive_dest to XLogDir + */ + sprintf(restoreXlog, "%08X%08X", log, seg); + snprintf(xlogRestoreCmd, MAXPGPATH, XLogArchRestoreProgram, + XLogArchiveDest, restoreXlog, recoveryXlog); + if (XLogArchiveDEBUG) + elog(LOG, "redo: system(%s)", xlogRestoreCmd); + + rc = system(xlogRestoreCmd); + if (rc!=0) { + /* + * remember, we rollforward UNTIL the restore fails + * so failure here is just part of the process... + * that makes it difficult to determine whether the restore + * failed because there isn't an archive to restore, or + * because the administrator has specified the restore + * program incorrectly... + * we could try to restore the testfile that the archiver writes + * when it starts up, but the absence of that file isn't + * very reliable evidence that the restore itself is broken, + * so just trust that the administrator has it correctly, + * XXX enhance that later + */ + elog(LOG, "redo: cannot restore \"%s\" from archive", restoreXlog); + /* + * if an archived file is not available, there might just be + * a partially full version of this file still in XLogDir + * so return this as the filename to open. + * In many recovery scenarios we expect this to fail also... + */ + snprintf(recoveryXlog, MAXPGPATH, "%s/%s", XLogDir, restoreXlog); + UseArchiveFirst = false; + if (stat(recoveryXlog, &stat_buf) == 0) { + elog(LOG, "redo: archive chain ends; using local copy of \"%s\"", restoreXlog); + } + /* + * if this file isn't available, then we need to setup the previous + * restored xlog to be the last and current xlog, if it exists + * remember: we've been restoring from recoverXlog, which isn't + * named the same as the normal xlog chain... + * also remember to output a corresponding archive_status of .done + */ + else if ((stat(lastrecoXlog, &stat_buf) == 0) && log==0 && seg > 0) { + prevlog = log; + prevseg = seg; + PrevLogSeg(prevlog, prevseg); + XLogFileName(tmppath, prevlog, prevseg); + elog(LOG, "redo: moving last restored xlog to \"%s\"", tmppath); + rc = rename(lastrecoXlog, tmppath); + if (rc!=0) { + elog(LOG, "redo: rename failed"); + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not open file \"%s\" (log file %u, segment %u): %m", + tmpXlog, log, seg))); + } + + /* + * write out an archive_status file for previous xlog + * to allow xlog to be recycled when recovered database + * is all up and working again + * ...looks wrong, but checkpointer is smart enough + * not to archive the current xlog! + */ + sprintf(tmpXlog, "%08X%08X", prevlog, prevseg); + snprintf(tmppath, MAXPGPATH, "%s/%s.done", RLogDir, tmpXlog); + rlogFD = AllocateFile(tmppath, "w"); + if (rlogFD == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write archive_status file \"%s\" ", + tmppath))); + FreeFile(rlogFD); + } + /* + * there is NO else here...we just return the filename + * knowing that it isn't there...which then throws the usual error, + * will end with a clear message as to why...but not a problem + */ + } + else { + /* restore success */ + /* + * if backup restored an xlog, yet we didnt use the local copy + * because we used the xlog version of that name from the + * archive instead, we need to write out an archive_status for + * it to show it can be recycled later + */ + XLogFileName(tmppath, log, seg); + if (stat(tmppath, &stat_buf) == 0) { + sprintf(tmpXlog, "%08X%08X", log, seg); + snprintf(tmppath, MAXPGPATH, "%s/%s.done", RLogDir, tmpXlog); + rlogFD = AllocateFile(tmppath, "w"); + if (rlogFD == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write archive_status file \"%s\" ", + tmppath))); + FreeFile(rlogFD); + } + elog(LOG, "redo: restored log file \"%s\" from archive", restoreXlog); + } + strcpy(path, recoveryXlog); + return; + } + + /* * Preallocate log files beyond the specified log endpoint, according to * the XLOGfile user parameter. */ *************** *** 1746,1751 **** --- 2079,2085 ---- struct dirent *xlde; char lastoff[32]; char path[MAXPGPATH]; + bool recycle=false; XLByteToPrevSeg(endptr, endlogId, endlogSeg); *************** *** 1761,1785 **** errno = 0; while ((xlde = readdir(xldir)) != NULL) { if (strlen(xlde->d_name) == 16 && strspn(xlde->d_name, "0123456789ABCDEF") == 16 && strcmp(xlde->d_name, lastoff) <= 0) { snprintf(path, MAXPGPATH, "%s/%s", XLogDir, xlde->d_name); ! if (XLOG_archive_dir[0]) ! { ! ereport(LOG, ! (errmsg("archiving transaction log file \"%s\"", ! xlde->d_name))); ! elog(WARNING, "archiving log files is not implemented"); ! } ! else { /* * Before deleting the file, see if it can be recycled as * a future log segment. We allow recycling segments up ! * to XLOGfileslop segments beyond the current XLOG ! * location. */ if (InstallXLogFileSegment(endlogId, endlogSeg, path, true, XLOGfileslop, --- 2095,2137 ---- errno = 0; while ((xlde = readdir(xldir)) != NULL) { + /* if correct length and alphanumeric makeup of file looks correct + * use the alphanumeric sorting property of the filenames to decide + * which ones are earlier than the lastoff transaction log + * ...maybe should read lastwrite datetime of lastoff, then check that + * only files last written earlier than this are removed/recycled + */ if (strlen(xlde->d_name) == 16 && strspn(xlde->d_name, "0123456789ABCDEF") == 16 && strcmp(xlde->d_name, lastoff) <= 0) { snprintf(path, MAXPGPATH, "%s/%s", XLogDir, xlde->d_name); ! if (XLogArchiveMode) { ! if (InRecoveryCleanup) ! /* ! * this allows recycling of transaction logs ! * during the shutdown checkpoint at end of recovery ! * - we may have restored logs that were not used ! * in the recovery sequence, and so will not have ! * had an archive_status file written for them. ! * - end-of-recovery doesn't clean up ALL xlogs, ! * which is why we also write archive_status files ! * as well as doing this ! */ ! recycle=true; ! else ! recycle=XLogArchiveDone(xlde->d_name); ! } ! else ! recycle=false; ! ! if ( recycle ) { /* * Before deleting the file, see if it can be recycled as * a future log segment. We allow recycling segments up ! * until there are XLOGfileslop segments beyond the ! * current XLOG location, otherwise they are removed. */ if (InstallXLogFileSegment(endlogId, endlogSeg, path, true, XLOGfileslop, *************** *** 1793,1802 **** { /* No need for any more future segments... */ ereport(LOG, ! (errmsg("removing transaction log file \"%s\"", xlde->d_name))); unlink(path); } } } errno = 0; --- 2145,2155 ---- { /* No need for any more future segments... */ ereport(LOG, ! (errmsg("too many transaction log files, removing \"%s\"", xlde->d_name))); unlink(path); } + XLogArchiveCleanup(xlde->d_name); } } errno = 0; *************** *** 2254,2259 **** --- 2607,2613 ---- { /* Init XLOG file paths */ snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir); + snprintf(RLogDir, MAXPGPATH, "%s/archive_status", XLogDir); snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); } *************** *** 2770,2775 **** --- 3124,3184 ---- } /* + * read in restore command from recovery.conf + * + * XXX longer term intention is to expand this to + * cater for additional parameters and controls + * possibly using a bison grammar to control it + */ + static void + readRecoveryCommandFile(void) + { + FILE *fd; + char *tok1 = NULL; + char *tok2 = NULL; + char *val = NULL; + char cmdline[MAXPGPATH]; + bool syntax_error = false; + + fd = AllocateFile(recoveryCommandFile, "r"); + if (fd == NULL) { + ereport(FATAL, + (errcode_for_file_access(), + errmsg("could not open recovery command file \"%s\"",recoveryCommandFile))); + return; + } + /* + * expecting |restore_program = Qcommand stringQ| + * e.g. |restore_program = 'cp %s/%s %s'| + * where | denote the beginning and end of the string + */ + fgets(cmdline, MAXPGPATH, fd); + + FreeFile(fd); + + tok1 = strtok(cmdline, "'"); + tok2 = strtok(NULL, "'"); + + if (tok1 != NULL && tok2 != NULL) { + val = tok2; + tok1 = strtok(cmdline, " ="); + if (strcmp(tok1,"restore_program") == 0) + strcpy(XLogArchRestoreProgram, tok2); + else + syntax_error = true; + } + else + syntax_error = true; + + if (syntax_error) + ereport(FATAL, + (errmsg("syntax error in \"%s\"", recoveryCommandFile), + errhint("Syntax needs to be like \"restore_program = 'cp %%s/%%s %%s'\""))); + + return; + } + + /* * This must be called ONCE during postmaster or standalone-backend startup */ void *************** *** 2785,2790 **** --- 3194,3200 ---- XLogRecord *record; char *buffer; uint32 freespace; + struct stat stat_buf; /* Use malloc() to ensure record buffer is MAXALIGNED */ buffer = (char *) malloc(_INTL_MAXLOGRECSZ); *************** *** 2831,2836 **** --- 3241,3278 ---- pg_usleep(60000000L); #endif + /* + * Check now for recovery.conf + * + * if this file exists, it demonstrates the intention of the administrator + * to recover this database using archived xlogs + * + * we do this now because the first xlog is about to be opened for the + * first time. We've read the checkpoint pointer from the control file + * and we are about to use that to open the xlog it points to, and + * will begin rollforward recovery from that point + */ + snprintf(recoveryCommandFile, MAXPGPATH, "%s/recovery.conf", DataDir); + if (stat(recoveryCommandFile, &stat_buf) == 0) { + + readRecoveryCommandFile(); + /* + * clearly indicate our state + */ + InArchiveRecovery = true; + /* + * set initial state for checking transaction logs + * this may change if the archive runs dry while still InArchiveRecovery + */ + UseArchiveFirst = true; + + ereport(LOG, + (errmsg("recovery command file found...starting archive recovery"))); + + if (XLogArchiveDEBUG) + elog(LOG,"restore_program = \"%s\"", XLogArchRestoreProgram); + } + /* * Get the last valid checkpoint record. If the latest one according * to pg_control is broken, try the next-to-last one. *************** *** 2861,2872 **** LastRec = RecPtr = checkPointLoc; memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); wasShutdown = (record->xl_info == XLOG_CHECKPOINT_SHUTDOWN); ! ereport(LOG, (errmsg("redo record is at %X/%X; undo record is at %X/%X; shutdown %s", checkPoint.redo.xlogid, checkPoint.redo.xrecoff, checkPoint.undo.xlogid, checkPoint.undo.xrecoff, ! wasShutdown ? "TRUE" : "FALSE"))); ereport(LOG, (errmsg("next transaction ID: %u; next OID: %u", checkPoint.nextXid, checkPoint.nextOid))); --- 3303,3322 ---- LastRec = RecPtr = checkPointLoc; memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); wasShutdown = (record->xl_info == XLOG_CHECKPOINT_SHUTDOWN); ! /* ! * we report the state of the control_file, not the checkpoint, why? ! * wasShutdown refers to whether the last checkpoint was a ! * shutdown checkpoint, NOT whether the database was shutdown ! * correctly according to control file. This distinction is only ! * important InArchiveRecovery, since otherwise we could ! * report that the database was shutdown, when the control file disagrees ! */ ereport(LOG, (errmsg("redo record is at %X/%X; undo record is at %X/%X; shutdown %s", checkPoint.redo.xlogid, checkPoint.redo.xrecoff, checkPoint.undo.xlogid, checkPoint.undo.xrecoff, ! (ControlFile->state == DB_SHUTDOWNED) ? "TRUE" : "FALSE"))); ! ereport(LOG, (errmsg("next transaction ID: %u; next OID: %u", checkPoint.nextXid, checkPoint.nextOid))); *************** *** 2914,2919 **** --- 3364,3373 ---- if (InRecovery) { int rmid; + char reclogpath[MAXPGPATH]; + bool recovery_debug_log = false; + int reclogFD = -1; + char *recbuf = NULL; ereport(LOG, (errmsg("database system was not properly shut down; " *************** *** 2922,2927 **** --- 3376,3382 ---- ControlFile->time = time(NULL); UpdateControlFile(); + /* Start up the recovery environment */ XLogInitRelationCache(); *************** *** 2933,2939 **** /* Is REDO required ? */ if (XLByteLT(checkPoint.redo, RecPtr)) ! record = ReadRecord(&(checkPoint.redo), PANIC, buffer); else { /* read past CheckPoint record */ --- 3388,3394 ---- /* Is REDO required ? */ if (XLByteLT(checkPoint.redo, RecPtr)) ! record = ReadRecord(&(checkPoint.redo), PANIC, buffer); else { /* read past CheckPoint record */ *************** *** 2946,2951 **** --- 3401,3423 ---- ereport(LOG, (errmsg("redo starts at %X/%X", ReadRecPtr.xlogid, ReadRecPtr.xrecoff))); + #ifdef WAL_DEBUG + if (XLOG_DEBUG) + recovery_debug_log = true; + #endif + if (XLogArchiveDEBUG) + recovery_debug_log = true; + + if (recovery_debug_log) { + recbuf = (char *) malloc(BLCKSZ); + snprintf(reclogpath, MAXPGPATH, "%s/recovery.log", DataDir); + unlink(reclogpath); + reclogFD = BasicOpenFile(reclogpath, O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR); + if (reclogFD < 0) + recovery_debug_log = false; + } + do { /* nextXid must be beyond record's xid */ *************** *** 2956,2976 **** TransactionIdAdvance(ShmemVariableCache->nextXid); } ! #ifdef WAL_DEBUG ! if (XLOG_DEBUG) { ! char buf[8192]; ! ! sprintf(buf, "REDO @ %X/%X; LSN %X/%X: ", ReadRecPtr.xlogid, ReadRecPtr.xrecoff, EndRecPtr.xlogid, EndRecPtr.xrecoff); ! xlog_outrec(buf, record); ! strcat(buf, " - "); ! RmgrTable[record->xl_rmid].rm_desc(buf, record->xl_info, XLogRecGetData(record)); ! elog(LOG, "%s", buf); } - #endif if (record->xl_info & XLR_BKP_BLOCK_MASK) RestoreBkpBlocks(record, EndRecPtr); --- 3428,3445 ---- TransactionIdAdvance(ShmemVariableCache->nextXid); } ! if (recovery_debug_log) { ! sprintf(recbuf, "\nREDO @ %X/%X; LSN %X/%X: ", ReadRecPtr.xlogid, ReadRecPtr.xrecoff, EndRecPtr.xlogid, EndRecPtr.xrecoff); ! xlog_outrec(recbuf, record); ! strcat(recbuf, " - "); ! RmgrTable[record->xl_rmid].rm_desc(recbuf, record->xl_info, XLogRecGetData(record)); ! ! write(reclogFD, recbuf, strlen(recbuf)); } if (record->xl_info & XLR_BKP_BLOCK_MASK) RestoreBkpBlocks(record, EndRecPtr); *************** *** 2978,2988 **** --- 3447,3467 ---- RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record); record = ReadRecord(NULL, LOG, buffer); } while (record != NULL); + + if (reclogFD >= 0) { + close(reclogFD); + free(recbuf); + } + ereport(LOG, (errmsg("redo done at %X/%X", ReadRecPtr.xlogid, ReadRecPtr.xrecoff))); LastRec = ReadRecPtr; InRedo = false; + if (InArchiveRecovery) + UseArchiveFirst = false; + InRecoveryCleanup = true; + InArchiveRecovery = false; } else ereport(LOG, *************** *** 3147,3152 **** --- 3626,3637 ---- * Okay, we're officially UP. */ InRecovery = false; + if (InRecoveryCleanup) { + unlink(recoveryCommandFile); + InRecoveryCleanup = false; + ereport(LOG, + (errmsg("archive recovery complete"))); + } ControlFile->state = DB_IN_PRODUCTION; ControlFile->time = time(NULL); *************** *** 3701,3707 **** strcat(buf, "UNKNOWN"); } - #ifdef WAL_DEBUG static void xlog_outrec(char *buf, XLogRecord *record) { --- 4186,4191 ---- *************** *** 3726,3733 **** sprintf(buf + strlen(buf), ": %s", RmgrTable[record->xl_rmid].rm_name); } - #endif /* WAL_DEBUG */ - /* * GUC support --- 4210,4215 ----
---------------------------(end of broadcast)--------------------------- TIP 4: Don't 'kill -9' the postmaster