/* * XLogArchiveCheckDone * * This is called when we are ready to delete or recycle an old XLOG segment * file or backup history file. If it is okay to delete it then return true. * If it is not time to delete it, make sure a .ready file exists, and return * false. * * If <XLOG>.done exists, then return true; else if <XLOG>.ready exists, * then return false; else create <XLOG>.ready and return false. * * The reason we do things this way is so that if the original attempt to * create <XLOG>.ready fails, we'll retry during subsequent checkpoints. */ bool XLogArchiveCheckDone(const char *xlog) { char archiveStatusPath[MAXPGPATH]; struct stat stat_buf; /* Always deletable if archiving is off */ if (!XLogArchivingActive()) return true; /* First check for .done --- this means archiver is done with it */ StatusFilePath(archiveStatusPath, xlog, ".done"); if (stat(archiveStatusPath, &stat_buf) == 0) return true; /* check for .ready --- this means archiver is still busy with it */ StatusFilePath(archiveStatusPath, xlog, ".ready"); if (stat(archiveStatusPath, &stat_buf) == 0) return false; /* Race condition --- maybe archiver just finished, so recheck */ StatusFilePath(archiveStatusPath, xlog, ".done"); if (stat(archiveStatusPath, &stat_buf) == 0) return true; /* Retry creation of the .ready file */ XLogArchiveNotify(xlog); return false; }
/* * Convenience routine to notify using segment number representation of filename */ void XLogArchiveNotifySeg(XLogSegNo segno) { char xlog[MAXFNAMELEN]; XLogFileName(xlog, ThisTimeLineID, segno); XLogArchiveNotify(xlog); }
/* * A file was restored from the archive under a temporary filename (path), * and now we want to keep it. Rename it under the permanent filename in * in pg_xlog (xlogfname), replacing any existing file with the same name. */ void KeepFileRestoredFromArchive(char *path, char *xlogfname) { char xlogfpath[MAXPGPATH]; bool reload = false; struct stat statbuf; snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname); if (stat(xlogfpath, &statbuf) == 0) { char oldpath[MAXPGPATH]; #ifdef WIN32 static unsigned int deletedcounter = 1; /* * On Windows, if another process (e.g a walsender process) holds the * file open in FILE_SHARE_DELETE mode, unlink will succeed, but the * file will still show up in directory listing until the last handle * is closed, and we cannot rename the new file in its place until * that. To avoid that problem, rename the old file to a temporary * name first. Use a counter to create a unique filename, because the * same file might be restored from the archive multiple times, and a * walsender could still be holding onto an old deleted version of it. */ snprintf(oldpath, MAXPGPATH, "%s.deleted%u", xlogfpath, deletedcounter++); if (rename(xlogfpath, oldpath) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m", xlogfpath, oldpath))); } #else /* same-size buffers, so this never truncates */ strlcpy(oldpath, xlogfpath, MAXPGPATH); #endif if (unlink(oldpath) != 0) ereport(FATAL, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", xlogfpath))); reload = true; } if (rename(path, xlogfpath) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m", path, xlogfpath))); /* * Create .done file forcibly to prevent the restored segment from being * archived again later. */ if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS) XLogArchiveForceDone(xlogfname); else XLogArchiveNotify(xlogfname); /* * If the existing file was replaced, since walsenders might have it open, * request them to reload a currently-open segment. This is only required * for WAL segments, walsenders don't hold other files open, but there's * no harm in doing this too often, and we don't know what kind of a file * we're dealing with here. */ if (reload) WalSndRqstFileReload(); /* * Signal walsender that new WAL has arrived. Again, this isn't necessary * if we restored something other than a WAL segment, but it does no harm * either. */ WalSndWakeup(); }