📄 xlog.c
字号:
* bad LSN on a data page. */ if (XLByteLT(LogwrtResult.Flush, record)) elog(InRecovery ? WARNING : ERROR, "xlog flush request %X/%X is not satisfied --- flushed only to %X/%X", record.xlogid, record.xrecoff, LogwrtResult.Flush.xlogid, LogwrtResult.Flush.xrecoff);}/* * Create a new XLOG file segment, or open a pre-existing one. * * log, seg: identify segment to be created/opened. * * *use_existent: if TRUE, OK to use a pre-existing file (else, any * pre-existing file will be deleted). On return, TRUE if a pre-existing * file was used. * * use_lock: if TRUE, acquire ControlFileLock while moving file into * place. This should be TRUE except during bootstrap log creation. The * caller must *not* hold the lock at call. * * Returns FD of opened file. */static intXLogFileInit(uint32 log, uint32 seg, bool *use_existent, bool use_lock){ char path[MAXPGPATH]; char tmppath[MAXPGPATH]; char zbuffer[BLCKSZ]; int fd; int nbytes; XLogFileName(path, log, seg); /* * Try to use existent file (checkpoint maker may have created it * already) */ if (*use_existent) { fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT, S_IRUSR | S_IWUSR); if (fd < 0) { if (errno != ENOENT) ereport(PANIC, (errcode_for_file_access(), errmsg("could not open file \"%s\" (log file %u, segment %u): %m", path, log, seg))); } else return (fd); } /* * Initialize an empty (all zeroes) segment. NOTE: it is possible * that another process is doing the same thing. If so, we will end * up pre-creating an extra log segment. That seems OK, and better * than holding the lock throughout this lengthy process. */ snprintf(tmppath, MAXPGPATH, "%s/xlogtemp.%d", XLogDir, (int) getpid()); unlink(tmppath); /* do not use XLOG_SYNC_BIT here --- want to fsync only at end of fill */ fd = BasicOpenFile(tmppath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR); if (fd < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", tmppath))); /* * Zero-fill the file. We have to do this the hard way to ensure that * all the file space has really been allocated --- on platforms that * allow "holes" in files, just seeking to the end doesn't allocate * intermediate space. This way, we know that we have all the space * and (after the fsync below) that all the indirect blocks are down * on disk. Therefore, fdatasync(2) or O_DSYNC will be sufficient to * sync future writes to the log file. */ MemSet(zbuffer, 0, sizeof(zbuffer)); for (nbytes = 0; nbytes < XLogSegSize; nbytes += sizeof(zbuffer)) { errno = 0; if ((int) write(fd, zbuffer, sizeof(zbuffer)) != (int) sizeof(zbuffer)) { int save_errno = errno; /* * If we fail to make the file, delete it to release disk * space */ unlink(tmppath); /* if write didn't set errno, assume problem is no disk space */ errno = save_errno ? save_errno : ENOSPC; ereport(PANIC, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", tmppath))); } } if (pg_fsync(fd) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", tmppath))); close(fd); /* * Now move the segment into place with its final name. * * If caller didn't want to use a pre-existing file, get rid of any * pre-existing file. Otherwise, cope with possibility that someone * else has created the file while we were filling ours: if so, use * ours to pre-create a future log segment. */ if (!InstallXLogFileSegment(log, seg, tmppath, *use_existent, XLOGfileslop, use_lock)) { /* No need for any more future segments... */ unlink(tmppath); } /* Set flag to tell caller there was no existent file */ *use_existent = false; /* Now open original target segment (might not be file I just made) */ fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT, S_IRUSR | S_IWUSR); if (fd < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not open file \"%s\" (log file %u, segment %u): %m", path, log, seg))); return (fd);}/* * Install a new XLOG segment file as a current or future log segment. * * This is used both to install a newly-created segment (which has a temp * filename while it's being created) and to recycle an old segment. * * log, seg: identify segment to install as (or first possible target). * * tmppath: initial name of file to install. It will be renamed into place. * * find_free: if TRUE, install the new segment at the first empty log/seg * number at or after the passed numbers. If FALSE, install the new segment * exactly where specified, deleting any existing segment file there. * * max_advance: maximum number of log/seg slots to advance past the starting * point. Fail if no free slot is found in this range. (Irrelevant if * find_free is FALSE.) * * use_lock: if TRUE, acquire ControlFileLock while moving file into * place. This should be TRUE except during bootstrap log creation. The * caller must *not* hold the lock at call. * * Returns TRUE if file installed, FALSE if not installed because of * exceeding max_advance limit. (Any other kind of failure causes ereport().) */static boolInstallXLogFileSegment(uint32 log, uint32 seg, char *tmppath, bool find_free, int max_advance, bool use_lock){ char path[MAXPGPATH]; struct stat stat_buf; XLogFileName(path, log, seg); /* * We want to be sure that only one process does this at a time. */ if (use_lock) LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); if (!find_free) { /* Force installation: get rid of any pre-existing segment file */ unlink(path); } else { /* Find a free slot to put it in */ while (stat(path, &stat_buf) == 0) { if (--max_advance < 0) { /* Failed to find a free slot within specified range */ if (use_lock) LWLockRelease(ControlFileLock); return false; } NextLogSeg(log, seg); XLogFileName(path, log, seg); } } /* * Prefer link() to rename() here just to be really sure that we don't * overwrite an existing logfile. However, there shouldn't be one, so * rename() is an acceptable substitute except for the truly paranoid. */#if HAVE_WORKING_LINK if (link(tmppath, path) < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not link file \"%s\" to \"%s\" (initialization of log file %u, segment %u): %m", tmppath, path, log, seg))); unlink(tmppath);#else if (rename(tmppath, path) < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\" (initialization of log file %u, segment %u): %m", tmppath, path, log, seg)));#endif if (use_lock) LWLockRelease(ControlFileLock); return true;}/* * Open a pre-existing logfile segment. */static intXLogFileOpen(uint32 log, uint32 seg, bool econt){ char path[MAXPGPATH]; int fd; XLogFileName(path, log, seg); fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT, S_IRUSR | S_IWUSR); if (fd < 0) { if (econt && errno == ENOENT) { ereport(LOG, (errcode_for_file_access(), errmsg("could not open file \"%s\" (log file %u, segment %u): %m", path, log, seg))); return (fd); } ereport(PANIC, (errcode_for_file_access(), 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. */static voidPreallocXlogFiles(XLogRecPtr endptr){ uint32 _logId; uint32 _logSeg; int lf; bool use_existent; XLByteToPrevSeg(endptr, _logId, _logSeg); if ((endptr.xrecoff - 1) % XLogSegSize >= (uint32) (0.75 * XLogSegSize)) { NextLogSeg(_logId, _logSeg); use_existent = true; lf = XLogFileInit(_logId, _logSeg, &use_existent, true); close(lf); }}/* * Remove or move offline all log files older or equal to passed log/seg# * * endptr is current (or recent) end of xlog; this is used to determine * whether we want to recycle rather than delete no-longer-wanted log files. */static voidMoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr){ uint32 endlogId; uint32 endlogSeg; DIR *xldir; struct dirent *xlde; char lastoff[32]; char path[MAXPGPATH]; XLByteToPrevSeg(endptr, endlogId, endlogSeg); xldir = AllocateDir(XLogDir); if (xldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open transaction log directory \"%s\": %m", XLogDir))); sprintf(lastoff, "%08X%08X", log, seg); 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, true)) { ereport(LOG, (errmsg("recycled transaction log file \"%s\"", xlde->d_name))); } else { /* No need for any more future segments... */ ereport(LOG, (errmsg("removing transaction log file \"%s\"", xlde->d_name))); unlink(path); } } } errno = 0; } if (errno) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read transaction log directory \"%s\": %m", XLogDir))); FreeDir(xldir);}/* * Restore the backup blocks present in an XLOG record, if any. * * We assume all of the record has been read into memory at *record. */static voidRestoreBkpBlocks(XLogRecord *record, XLogRecPtr lsn){ Relation reln; Buffer buffer; Page page; BkpBlock bkpb; char *blk; int i; blk = (char *) XLogRecGetData(record) + record->xl_len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { if (!(record->xl_info & XLR_SET_BKP_BLOCK(i))) continue; memcpy((char *) &bkpb, blk, sizeof(BkpBlock)); blk += sizeof(BkpBlock); reln = XLogOpenRelation(true, record->xl_rmid, bkpb.node); if (reln) { buffer = XLogReadBuffer(true, reln, bkpb.block); if (BufferIsValid(buffer)) { page = (Page) BufferGetPage(buffer); memcpy((char *) page, blk, BLCKSZ); PageSetLSN(page, lsn); PageSetSUI(page, ThisStartUpID); UnlockAndWriteBuffer(buffer); } } blk += BLCKSZ; }}/* * CRC-check an XLOG record. We do not believe the contents of an XLOG * record (other than to the minimal extent of computing the amount of * data to read in) until we've checked the CRCs. * * We assume all of the record has been read into memory at *record. */static boolRecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode){ crc64 crc; crc64 cbuf; int i; uint32 len = record->xl_len; char *blk; /* Check CRC of rmgr data and record header */ INIT_CRC64(crc); COMP_CRC64(crc, XLogRecGetData(record), len); COMP_CRC64(crc, (char *) record + sizeof(crc64), SizeOfXLogRecord - sizeof(crc64)); FIN_CRC64(crc); if (!EQ_CRC64(record->xl_crc, crc)) { ereport(emode, (errmsg("incorrect resource manager data checksum in record at %X/%X", recptr.xlogid, recptr.xrecoff)));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -