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

📄 slru.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
		slru_errcause = SLRU_READ_FAILED;		slru_errno = errno;		close(fd);		return false;	}	if (close(fd))	{		slru_errcause = SLRU_CLOSE_FAILED;		slru_errno = errno;		return false;	}	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 * independent read/write operations.  We do batch operations during * SimpleLruFlush, though. * * fdata is NULL for a standalone write, pointer to open-file info during * SimpleLruFlush. */static boolSlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata){	SlruShared	shared = 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 = -1;	/*	 * During a Flush, we may already have the desired file open.	 */	if (fdata)	{		int			i;		for (i = 0; i < fdata->num_files; i++)		{			if (fdata->segno[i] == segno)			{				fd = fdata->fd[i];				break;			}		}	}	if (fd < 0)	{		/*		 * 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.)		 *		 * Note: it is possible for more than one backend to be executing		 * this code simultaneously for different pages of the same file.		 * Hence, don't use O_EXCL or O_TRUNC or anything like that.		 */		SlruFileName(ctl, path, segno);		fd = BasicOpenFile(path, O_RDWR | O_CREAT | PG_BINARY,						   S_IRUSR | S_IWUSR);		if (fd < 0)		{			slru_errcause = SLRU_OPEN_FAILED;			slru_errno = errno;			return false;		}		if (fdata)		{			fdata->fd[fdata->num_files] = fd;			fdata->segno[fdata->num_files] = segno;			fdata->num_files++;		}	}	if (lseek(fd, (off_t) offset, SEEK_SET) < 0)	{		slru_errcause = SLRU_SEEK_FAILED;		slru_errno = errno;		if (!fdata)			close(fd);		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;		if (!fdata)			close(fd);		return false;	}	/*	 * If not part of Flush, need to fsync now.  We assume this happens	 * infrequently enough that it's not a performance issue.	 */	if (!fdata)	{		if (ctl->do_fsync && pg_fsync(fd))		{			slru_errcause = SLRU_FSYNC_FAILED;			slru_errno = errno;			close(fd);			return false;		}		if (close(fd))		{			slru_errcause = SLRU_CLOSE_FAILED;			slru_errno = errno;			return false;		}	}	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_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;		case SLRU_FSYNC_FAILED:			ereport(ERROR,					(errcode_for_file_access(),					 errmsg("could not access status of transaction %u", xid),					 errdetail("could not fsync file \"%s\": %m",							   path)));			break;		case SLRU_CLOSE_FAILED:			ereport(ERROR,					(errcode_for_file_access(),					 errmsg("could not access status of transaction %u", xid),					 errdetail("could not close file \"%s\": %m",							   path)));			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 = 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_SLRU_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_SLRU_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 just wait for someone else to complete the		 * I/O, which we can do by waiting for the per-buffer lock.		 */		if (shared->page_status[bestslot] == SLRU_PAGE_READ_IN_PROGRESS)		{			LWLockRelease(shared->ControlLock);			LWLockAcquire(shared->buffer_locks[bestslot], LW_SHARED);			LWLockRelease(shared->buffer_locks[bestslot]);			LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);		}		else			SimpleLruWritePage(ctl, bestslot, NULL);		/*		 * 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.		 */	}}/* * Flush dirty pages to disk during checkpoint or database shutdown */voidSimpleLruFlush(SlruCtl ctl, bool checkpoint){	SlruShared	shared = ctl->shared;	SlruFlushData fdata;	int			slotno;	int			pageno = 0;	int			i;	bool		ok;	/*	 * Find and write dirty pages	 */	fdata.num_files = 0;	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);	for (slotno = 0; slotno < NUM_SLRU_BUFFERS; slotno++)	{		SimpleLruWritePage(ctl, slotno, &fdata);		/*		 * 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(shared->ControlLock);	/*	 * Now fsync and close any files that were open	 */	ok = true;	for (i = 0; i < fdata.num_files; i++)	{		if (ctl->do_fsync && pg_fsync(fdata.fd[i]))		{			slru_errcause = SLRU_FSYNC_FAILED;			slru_errno = errno;			pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;			ok = false;		}		if (close(fdata.fd[i]))		{			slru_errcause = SLRU_CLOSE_FAILED;			slru_errno = errno;			pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;			ok = false;		}	}	if (!ok)		SlruReportIOError(ctl, pageno, InvalidTransactionId);}/* * Remove all segments before the one holding the passed page number */voidSimpleLruTruncate(SlruCtl ctl, int cutoffPage){	SlruShared	shared = ctl->shared;	int			slotno;	/*	 * The cutoff point is the start of the segment containing cutoffPage.	 */	cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT;	/*	 * Scan shared memory and remove any pages preceding the cutoff page, to	 * ensure we won't rewrite them later.  (Since this is normally called in	 * or just after a checkpoint, any dirty pages should have been flushed	 * already ... we're just being extra careful here.)	 */	LWLockAcquire(shared->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(shared->ControlLock);		ereport(LOG,		  (errmsg("could not truncate directory \"%s\": apparent wraparound",				  ctl->Dir)));		return;	}	for (slotno = 0; slotno < NUM_SLRU_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)		{			LWLockRelease(shared->ControlLock);			LWLockAcquire(shared->buffer_locks[slotno], LW_SHARED);			LWLockRelease(shared->buffer_locks[slotno]);			LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);		}		else			SimpleLruWritePage(ctl, slotno, NULL);		goto restart;	}	LWLockRelease(shared->ControlLock);	/* Now we can remove the old segment(s) */	(void) SlruScanDirectory(ctl, cutoffPage, true);}/* * SimpleLruTruncate 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. * * This can be called directly from clog.c, for reasons explained there. */boolSlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions){	bool		found = false;	DIR		   *cldir;	struct dirent *clde;	int			segno;	int			segpage;	char		path[MAXPGPATH];	/*	 * The cutoff point is the start of the segment containing cutoffPage.	 * (This is redundant when called from SimpleLruTruncate, but not when	 * called directly from clog.c.)	 */	cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT;	cldir = AllocateDir(ctl->Dir);	while ((clde = ReadDir(cldir, ctl->Dir)) != 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)				{					snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, clde->d_name);					ereport(DEBUG2,							(errmsg("removing file \"%s\"", path)));					unlink(path);				}			}		}	}	FreeDir(cldir);	return found;}

⌨️ 快捷键说明

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