⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 slru.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 2 页
字号:
 * * For now, assume it's not worth keeping a file pointer open across * read/write operations.  We could cache one virtual file pointer ... */static boolSlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno){	SlruShared	shared = (SlruShared) ctl->shared;	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;	int			offset = rpageno * BLCKSZ;	char		path[MAXPGPATH];	int			fd;	SlruFileName(ctl, path, segno);	/*	 * In a crash-and-restart situation, it's possible for us to receive	 * commands to set the commit status of transactions whose bits are in	 * already-truncated segments of the commit log (see notes in	 * SlruPhysicalWritePage).	Hence, if we are InRecovery, allow the	 * case where the file doesn't exist, and return zeroes instead.	 */	fd = BasicOpenFile(path, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);	if (fd < 0)	{		if (errno != ENOENT || !InRecovery)		{			slru_errcause = SLRU_OPEN_FAILED;			slru_errno = errno;			return false;		}		ereport(LOG,				(errmsg("file \"%s\" doesn't exist, reading as zeroes",						path)));		MemSet(shared->page_buffer[slotno], 0, BLCKSZ);		return true;	}	if (lseek(fd, (off_t) offset, SEEK_SET) < 0)	{		slru_errcause = SLRU_SEEK_FAILED;		slru_errno = errno;		return false;	}	errno = 0;	if (read(fd, shared->page_buffer[slotno], BLCKSZ) != BLCKSZ)	{		slru_errcause = SLRU_READ_FAILED;		slru_errno = errno;		return false;	}	close(fd);	return true;}/* * Physical write of a page from a buffer slot * * On failure, we cannot just ereport(ERROR) since caller has put state in * shared memory that must be undone.  So, we return FALSE and save enough * info in static variables to let SlruReportIOError make the report. * * For now, assume it's not worth keeping a file pointer open across * read/write operations.  We could cache one virtual file pointer ... */static boolSlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno){	SlruShared	shared = (SlruShared) ctl->shared;	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;	int			offset = rpageno * BLCKSZ;	char		path[MAXPGPATH];	int			fd;	SlruFileName(ctl, path, segno);	/*	 * If the file doesn't already exist, we should create it.  It is	 * possible for this to need to happen when writing a page that's not	 * first in its segment; we assume the OS can cope with that.  (Note:	 * it might seem that it'd be okay to create files only when	 * SimpleLruZeroPage is called for the first page of a segment.	 * However, if after a crash and restart the REDO logic elects to	 * replay the log from a checkpoint before the latest one, then it's	 * possible that we will get commands to set transaction status of	 * transactions that have already been truncated from the commit log.	 * Easiest way to deal with that is to accept references to	 * nonexistent files here and in SlruPhysicalReadPage.)	 */	fd = BasicOpenFile(path, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);	if (fd < 0)	{		if (errno != ENOENT)		{			slru_errcause = SLRU_OPEN_FAILED;			slru_errno = errno;			return false;		}		fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,						   S_IRUSR | S_IWUSR);		if (fd < 0)		{			slru_errcause = SLRU_CREATE_FAILED;			slru_errno = errno;			return false;		}	}	if (lseek(fd, (off_t) offset, SEEK_SET) < 0)	{		slru_errcause = SLRU_SEEK_FAILED;		slru_errno = errno;		return false;	}	errno = 0;	if (write(fd, shared->page_buffer[slotno], BLCKSZ) != BLCKSZ)	{		/* if write didn't set errno, assume problem is no disk space */		if (errno == 0)			errno = ENOSPC;		slru_errcause = SLRU_WRITE_FAILED;		slru_errno = errno;		return false;	}	close(fd);	return true;}/* * Issue the error message after failure of SlruPhysicalReadPage or * SlruPhysicalWritePage.  Call this after cleaning up shared-memory state. */static voidSlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid){	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;	int			offset = rpageno * BLCKSZ;	char		path[MAXPGPATH];	SlruFileName(ctl, path, segno);	errno = slru_errno;	switch (slru_errcause)	{		case SLRU_OPEN_FAILED:			ereport(ERROR,					(errcode_for_file_access(),				errmsg("could not access status of transaction %u", xid),					 errdetail("could not open file \"%s\": %m",							   path)));			break;		case SLRU_CREATE_FAILED:			ereport(ERROR,					(errcode_for_file_access(),				errmsg("could not access status of transaction %u", xid),					 errdetail("could not create file \"%s\": %m",							   path)));			break;		case SLRU_SEEK_FAILED:			ereport(ERROR,					(errcode_for_file_access(),				errmsg("could not access status of transaction %u", xid),				  errdetail("could not seek in file \"%s\" to offset %u: %m",							path, offset)));			break;		case SLRU_READ_FAILED:			ereport(ERROR,					(errcode_for_file_access(),				errmsg("could not access status of transaction %u", xid),				   errdetail("could not read from file \"%s\" at offset %u: %m",							 path, offset)));			break;		case SLRU_WRITE_FAILED:			ereport(ERROR,					(errcode_for_file_access(),				errmsg("could not access status of transaction %u", xid),				  errdetail("could not write to file \"%s\" at offset %u: %m",							path, offset)));			break;		default:			/* can't get here, we trust */			elog(ERROR, "unrecognized SimpleLru error cause: %d",				 (int) slru_errcause);			break;	}}/* * Select the slot to re-use when we need a free slot. * * The target page number is passed because we need to consider the * possibility that some other process reads in the target page while * we are doing I/O to free a slot.  Hence, check or recheck to see if * any slot already holds the target page, and return that slot if so. * Thus, the returned slot is *either* a slot already holding the pageno * (could be any state except EMPTY), *or* a freeable slot (state EMPTY * or CLEAN). * * Control lock must be held at entry, and will be held at exit. */static intSlruSelectLRUPage(SlruCtl ctl, int pageno){	SlruShared	shared = (SlruShared) ctl->shared;	/* Outer loop handles restart after I/O */	for (;;)	{		int			slotno;		int			bestslot = 0;		unsigned int bestcount = 0;		/* See if page already has a buffer assigned */		for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)		{			if (shared->page_number[slotno] == pageno &&				shared->page_status[slotno] != SLRU_PAGE_EMPTY)				return slotno;		}		/*		 * If we find any EMPTY slot, just select that one. Else locate		 * the least-recently-used slot that isn't the latest page.		 */		for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)		{			if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)				return slotno;			if (shared->page_lru_count[slotno] > bestcount &&				shared->page_number[slotno] != shared->latest_page_number)			{				bestslot = slotno;				bestcount = shared->page_lru_count[slotno];			}		}		/*		 * If the selected page is clean, we're set.		 */		if (shared->page_status[bestslot] == SLRU_PAGE_CLEAN)			return bestslot;		/*		 * We need to do I/O.  Normal case is that we have to write it		 * out, but it's possible in the worst case to have selected a		 * read-busy page.	In that case we use SimpleLruReadPage to wait		 * for the read to complete.		 */		if (shared->page_status[bestslot] == SLRU_PAGE_READ_IN_PROGRESS)			(void) SimpleLruReadPage(ctl, shared->page_number[bestslot],									 InvalidTransactionId, false);		else			SimpleLruWritePage(ctl, bestslot);		/*		 * Now loop back and try again.  This is the easiest way of		 * dealing with corner cases such as the victim page being		 * re-dirtied while we wrote it.		 */	}}/* * This must be called ONCE during postmaster or standalone-backend startup */voidSimpleLruSetLatestPage(SlruCtl ctl, int pageno){	SlruShared	shared = (SlruShared) ctl->shared;	shared->latest_page_number = pageno;}/* * This is called during checkpoint and postmaster/standalone-backend shutdown */voidSimpleLruFlush(SlruCtl ctl, bool checkpoint){#ifdef USE_ASSERT_CHECKING		/* only used in Assert() */	SlruShared	shared = (SlruShared) ctl->shared;#endif	int			slotno;	LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);	for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)	{		SimpleLruWritePage(ctl, slotno);		/*		 * When called during a checkpoint, we cannot assert that the slot		 * is clean now, since another process might have re-dirtied it		 * already.  That's okay.		 */		Assert(checkpoint ||			   shared->page_status[slotno] == SLRU_PAGE_EMPTY ||			   shared->page_status[slotno] == SLRU_PAGE_CLEAN);	}	LWLockRelease(ctl->locks->ControlLock);}/* * Remove all segments before the one holding the passed page number * * When this is called, we know that the database logically contains no * reference to transaction IDs older than oldestXact.	However, we must * not remove any segment until we have performed a checkpoint, to ensure * that no such references remain on disk either; else a crash just after * the truncation might leave us with a problem.  Since CLOG segments hold * a large number of transactions, the opportunity to actually remove a * segment is fairly rare, and so it seems best not to do the checkpoint * unless we have confirmed that there is a removable segment.	Therefore * we issue the checkpoint command here, not in higher-level code as might * seem cleaner. */voidSimpleLruTruncate(SlruCtl ctl, int cutoffPage){	int			slotno;	SlruShared	shared = (SlruShared) ctl->shared;	/*	 * The cutoff point is the start of the segment containing cutoffPage.	 */	cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT;	if (!SlruScanDirectory(ctl, cutoffPage, false))		return;					/* nothing to remove */	/* Perform a forced CHECKPOINT */	CreateCheckPoint(false, true);	/*	 * Scan shared memory and remove any pages preceding the cutoff page,	 * to ensure we won't rewrite them later.  (Any dirty pages should	 * have been flushed already during the checkpoint, we're just being	 * extra careful here.)	 */	LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);restart:;	/*	 * While we are holding the lock, make an important safety check: the	 * planned cutoff point must be <= the current endpoint page.	 * Otherwise we have already wrapped around, and proceeding with the	 * truncation would risk removing the current segment.	 */	if (ctl->PagePrecedes(shared->latest_page_number, cutoffPage))	{		LWLockRelease(ctl->locks->ControlLock);		ereport(LOG,				(errmsg("could not truncate directory \"%s\": apparent wraparound",						ctl->Dir)));		return;	}	for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)	{		if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)			continue;		if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))			continue;		/*		 * If page is CLEAN, just change state to EMPTY (expected case).		 */		if (shared->page_status[slotno] == SLRU_PAGE_CLEAN)		{			shared->page_status[slotno] = SLRU_PAGE_EMPTY;			continue;		}		/*		 * Hmm, we have (or may have) I/O operations acting on the page,		 * so we've got to wait for them to finish and then start again.		 * This is the same logic as in SlruSelectLRUPage.		 */		if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)			(void) SimpleLruReadPage(ctl, shared->page_number[slotno],									 InvalidTransactionId, false);		else			SimpleLruWritePage(ctl, slotno);		goto restart;	}	LWLockRelease(ctl->locks->ControlLock);	/* Now we can remove the old segment(s) */	(void) SlruScanDirectory(ctl, cutoffPage, true);}/* * SlruTruncate subroutine: scan directory for removable segments. * Actually remove them iff doDeletions is true.  Return TRUE iff any * removable segments were found.  Note: no locking is needed. */static boolSlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions){	bool		found = false;	DIR		   *cldir;	struct dirent *clde;	int			segno;	int			segpage;	char		path[MAXPGPATH];	cldir = AllocateDir(ctl->Dir);	if (cldir == NULL)		ereport(ERROR,				(errcode_for_file_access(),			   errmsg("could not open directory \"%s\": %m", ctl->Dir)));	errno = 0;	while ((clde = readdir(cldir)) != NULL)	{		if (strlen(clde->d_name) == 4 &&			strspn(clde->d_name, "0123456789ABCDEF") == 4)		{			segno = (int) strtol(clde->d_name, NULL, 16);			segpage = segno * SLRU_PAGES_PER_SEGMENT;			if (ctl->PagePrecedes(segpage, cutoffPage))			{				found = true;				if (doDeletions)				{					ereport(LOG,							(errmsg("removing file \"%s/%s\"",									ctl->Dir, clde->d_name)));					snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, clde->d_name);					unlink(path);				}			}		}		errno = 0;	}	if (errno)		ereport(ERROR,				(errcode_for_file_access(),			   errmsg("could not read directory \"%s\": %m", ctl->Dir)));	FreeDir(cldir);	return found;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -