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

📄 multixact.c

📁 postgresql8.3.4源码,开源数据库
💻 C
📖 第 1 页 / 共 4 页
字号:
		 */		nextMXact = MultiXactState->nextMXact;		if (nextMXact < FirstMultiXactId)			nextMXact = FirstMultiXactId;		OldestMemberMXactId[MyBackendId] = nextMXact;		LWLockRelease(MultiXactGenLock);		debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",					MyBackendId, nextMXact);	}}/* * MultiXactIdSetOldestVisible *		Save the oldest MultiXactId this transaction considers possibly live. * * We set the OldestVisibleMXactId for a given transaction the first time * it's going to inspect any MultiXactId.  Once we have set this, we are * guaranteed that the checkpointer won't truncate off SLRU data for * MultiXactIds at or after our OldestVisibleMXactId. * * The value to set is the oldest of nextMXact and all the valid per-backend * OldestMemberMXactId[] entries.  Because of the locking we do, we can be * certain that no subsequent call to MultiXactIdSetOldestMember can set * an OldestMemberMXactId[] entry older than what we compute here.	Therefore * there is no live transaction, now or later, that can be a member of any * MultiXactId older than the OldestVisibleMXactId we compute here. */static voidMultiXactIdSetOldestVisible(void){	if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))	{		MultiXactId oldestMXact;		int			i;		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);		/*		 * We have to beware of the possibility that nextMXact is in the		 * wrapped-around state.  We don't fix the counter itself here, but we		 * must be sure to store a valid value in our array entry.		 */		oldestMXact = MultiXactState->nextMXact;		if (oldestMXact < FirstMultiXactId)			oldestMXact = FirstMultiXactId;		for (i = 1; i <= MaxBackends; i++)		{			MultiXactId thisoldest = OldestMemberMXactId[i];			if (MultiXactIdIsValid(thisoldest) &&				MultiXactIdPrecedes(thisoldest, oldestMXact))				oldestMXact = thisoldest;		}		OldestVisibleMXactId[MyBackendId] = oldestMXact;		LWLockRelease(MultiXactGenLock);		debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",					MyBackendId, oldestMXact);	}}/* * MultiXactIdWait *		Sleep on a MultiXactId. * * We do this by sleeping on each member using XactLockTableWait.  Any * members that belong to the current backend are *not* waited for, however; * this would not merely be useless but would lead to Assert failure inside * XactLockTableWait.  By the time this returns, it is certain that all * transactions *of other backends* that were members of the MultiXactId * are dead (and no new ones can have been added, since it is not legal * to add members to an existing MultiXactId). * * But by the time we finish sleeping, someone else may have changed the Xmax * of the containing tuple, so the caller needs to iterate on us somehow. */voidMultiXactIdWait(MultiXactId multi){	TransactionId *members;	int			nmembers;	nmembers = GetMultiXactIdMembers(multi, &members);	if (nmembers >= 0)	{		int			i;		for (i = 0; i < nmembers; i++)		{			TransactionId member = members[i];			debug_elog4(DEBUG2, "MultiXactIdWait: waiting for %d (%u)",						i, member);			if (!TransactionIdIsCurrentTransactionId(member))				XactLockTableWait(member);		}		pfree(members);	}}/* * ConditionalMultiXactIdWait *		As above, but only lock if we can get the lock without blocking. */boolConditionalMultiXactIdWait(MultiXactId multi){	bool		result = true;	TransactionId *members;	int			nmembers;	nmembers = GetMultiXactIdMembers(multi, &members);	if (nmembers >= 0)	{		int			i;		for (i = 0; i < nmembers; i++)		{			TransactionId member = members[i];			debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",						i, member);			if (!TransactionIdIsCurrentTransactionId(member))			{				result = ConditionalXactLockTableWait(member);				if (!result)					break;			}		}		pfree(members);	}	return result;}/* * CreateMultiXactId *		Make a new MultiXactId * * Make XLOG, SLRU and cache entries for a new MultiXactId, recording the * given TransactionIds as members.  Returns the newly created MultiXactId. * * NB: the passed xids[] array will be sorted in-place. */static MultiXactIdCreateMultiXactId(int nxids, TransactionId *xids){	MultiXactId multi;	MultiXactOffset offset;	XLogRecData rdata[2];	xl_multixact_create xlrec;	debug_elog3(DEBUG2, "Create: %s",				mxid_to_string(InvalidMultiXactId, nxids, xids));	/*	 * See if the same set of XIDs already exists in our cache; if so, just	 * re-use that MultiXactId.  (Note: it might seem that looking in our	 * cache is insufficient, and we ought to search disk to see if a	 * duplicate definition already exists.  But since we only ever create	 * MultiXacts containing our own XID, in most cases any such MultiXacts	 * were in fact created by us, and so will be in our cache.  There are	 * corner cases where someone else added us to a MultiXact without our	 * knowledge, but it's not worth checking for.)	 */	multi = mXactCacheGetBySet(nxids, xids);	if (MultiXactIdIsValid(multi))	{		debug_elog2(DEBUG2, "Create: in cache!");		return multi;	}	/*	 * Assign the MXID and offsets range to use, and make sure there is space	 * in the OFFSETs and MEMBERs files.  NB: this routine does	 * START_CRIT_SECTION().	 */	multi = GetNewMultiXactId(nxids, &offset);	/*	 * Make an XLOG entry describing the new MXID.	 *	 * Note: we need not flush this XLOG entry to disk before proceeding. The	 * only way for the MXID to be referenced from any data page is for	 * heap_lock_tuple() to have put it there, and heap_lock_tuple() generates	 * an XLOG record that must follow ours.  The normal LSN interlock between	 * the data page and that XLOG record will ensure that our XLOG record	 * reaches disk first.	If the SLRU members/offsets data reaches disk	 * sooner than the XLOG record, we do not care because we'll overwrite it	 * with zeroes unless the XLOG record is there too; see notes at top of	 * this file.	 */	xlrec.mid = multi;	xlrec.moff = offset;	xlrec.nxids = nxids;	rdata[0].data = (char *) (&xlrec);	rdata[0].len = MinSizeOfMultiXactCreate;	rdata[0].buffer = InvalidBuffer;	rdata[0].next = &(rdata[1]);	rdata[1].data = (char *) xids;	rdata[1].len = nxids * sizeof(TransactionId);	rdata[1].buffer = InvalidBuffer;	rdata[1].next = NULL;	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID, rdata);	/* Now enter the information into the OFFSETs and MEMBERs logs */	RecordNewMultiXact(multi, offset, nxids, xids);	/* Done with critical section */	END_CRIT_SECTION();	/* Store the new MultiXactId in the local cache, too */	mXactCachePut(multi, nxids, xids);	debug_elog2(DEBUG2, "Create: all done");	return multi;}/* * RecordNewMultiXact *		Write info about a new multixact into the offsets and members files * * This is broken out of CreateMultiXactId so that xlog replay can use it. */static voidRecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,				   int nxids, TransactionId *xids){	int			pageno;	int			prev_pageno;	int			entryno;	int			slotno;	MultiXactOffset *offptr;	int			i;	LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);	pageno = MultiXactIdToOffsetPage(multi);	entryno = MultiXactIdToOffsetEntry(multi);	/*	 * Note: we pass the MultiXactId to SimpleLruReadPage as the "transaction"	 * to complain about if there's any I/O error.  This is kinda bogus, but	 * since the errors will always give the full pathname, it should be clear	 * enough that a MultiXactId is really involved.  Perhaps someday we'll	 * take the trouble to generalize the slru.c error reporting code.	 */	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];	offptr += entryno;	*offptr = offset;	MultiXactOffsetCtl->shared->page_dirty[slotno] = true;	/* Exchange our lock */	LWLockRelease(MultiXactOffsetControlLock);	LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);	prev_pageno = -1;	for (i = 0; i < nxids; i++, offset++)	{		TransactionId *memberptr;		pageno = MXOffsetToMemberPage(offset);		entryno = MXOffsetToMemberEntry(offset);		if (pageno != prev_pageno)		{			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);			prev_pageno = pageno;		}		memberptr = (TransactionId *)			MultiXactMemberCtl->shared->page_buffer[slotno];		memberptr += entryno;		*memberptr = xids[i];		MultiXactMemberCtl->shared->page_dirty[slotno] = true;	}	LWLockRelease(MultiXactMemberControlLock);}/* * GetNewMultiXactId *		Get the next MultiXactId. * * Also, reserve the needed amount of space in the "members" area.	The * starting offset of the reserved space is returned in *offset. * * This may generate XLOG records for expansion of the offsets and/or members * files.  Unfortunately, we have to do that while holding MultiXactGenLock * to avoid race conditions --- the XLOG record for zeroing a page must appear * before any backend can possibly try to store data in that page! * * We start a critical section before advancing the shared counters.  The * caller must end the critical section after writing SLRU data. */static MultiXactIdGetNewMultiXactId(int nxids, MultiXactOffset *offset){	MultiXactId result;	MultiXactOffset nextOffset;	debug_elog3(DEBUG2, "GetNew: for %d xids", nxids);	/* MultiXactIdSetOldestMember() must have been called already */	Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));	LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);	/* Handle wraparound of the nextMXact counter */	if (MultiXactState->nextMXact < FirstMultiXactId)		MultiXactState->nextMXact = FirstMultiXactId;	/*	 * Assign the MXID, and make sure there is room for it in the file.	 */	result = MultiXactState->nextMXact;	ExtendMultiXactOffset(result);	/*	 * Reserve the members space, similarly to above.  Also, be careful not to	 * return zero as the starting offset for any multixact. See	 * GetMultiXactIdMembers() for motivation.	 */	nextOffset = MultiXactState->nextOffset;	if (nextOffset == 0)	{		*offset = 1;		nxids++;				/* allocate member slot 0 too */	}	else		*offset = nextOffset;	ExtendMultiXactMember(nextOffset, nxids);	/*	 * Critical section from here until caller has written the data into the	 * just-reserved SLRU space; we don't want to error out with a partly	 * written MultiXact structure.  (In particular, failing to write our	 * start offset after advancing nextMXact would effectively corrupt the	 * previous MultiXact.)	 */	START_CRIT_SECTION();	/*	 * Advance counters.  As in GetNewTransactionId(), this must not happen	 * until after file extension has succeeded!	 *	 * We don't care about MultiXactId wraparound here; it will be handled by	 * the next iteration.	But note that nextMXact may be InvalidMultiXactId	 * after this routine exits, so anyone else looking at the variable must	 * be prepared to deal with that.  Similarly, nextOffset may be zero, but	 * we won't use that as the actual start offset of the next multixact.	 */	(MultiXactState->nextMXact)++;	MultiXactState->nextOffset += nxids;	LWLockRelease(MultiXactGenLock);	debug_elog4(DEBUG2, "GetNew: returning %u offset %u", result, *offset);	return result;}/* * GetMultiXactIdMembers *		Returns the set of TransactionIds that make up a MultiXactId * * We return -1 if the MultiXactId is too old to possibly have any members * still running; in that case we have not actually looked them up, and * *xids is not set. */intGetMultiXactIdMembers(MultiXactId multi, TransactionId **xids){	int			pageno;	int			prev_pageno;	int			entryno;	int			slotno;	MultiXactOffset *offptr;	MultiXactOffset offset;	int			length;	int			truelength;	int			i;	MultiXactId nextMXact;	MultiXactId tmpMXact;	MultiXactOffset nextOffset;	TransactionId *ptr;	debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);	Assert(MultiXactIdIsValid(multi));	/* See if the MultiXactId is in the local cache */	length = mXactCacheGetById(multi, xids);	if (length >= 0)	{		debug_elog3(DEBUG2, "GetMembers: found %s in the cache",					mxid_to_string(multi, length, *xids));		return length;	}	/* Set our OldestVisibleMXactId[] entry if we didn't already */	MultiXactIdSetOldestVisible();	/*	 * We check known limits on MultiXact before resorting to the SLRU area.	 *	 * An ID older than our OldestVisibleMXactId[] entry can't possibly still	 * be running, and we'd run the risk of trying to read already-truncated	 * SLRU data if we did try to examine it.	 *	 * Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is	 * seen, it implies undetected ID wraparound has occurred.	We just	 * silently assume that such an ID is no longer running.	 *	 * Shared lock is enough here since we aren't modifying any global state.	 * Also, we can examine our own OldestVisibleMXactId without the lock,	 * since no one else is allowed to change it.	 */	if (MultiXactIdPrecedes(multi, OldestVisibleMXactId[MyBackendId]))	{		debug_elog2(DEBUG2, "GetMembers: it's too old");		*xids = NULL;		return -1;	}	/*	 * Acquire the shared lock just long enough to grab the current counter	 * values.	We may need both nextMXact and nextOffset; see below.	 */	LWLockAcquire(MultiXactGenLock, LW_SHARED);	nextMXact = MultiXactState->nextMXact;	nextOffset = MultiXactState->nextOffset;	LWLockRelease(MultiXactGenLock);	if (!MultiXactIdPrecedes(multi, nextMXact))	{		debug_elog2(DEBUG2, "GetMembers: it's too new!");		*xids = NULL;		return -1;	}	/*	 * Find out the offset at which we need to start reading MultiXactMembers	 * and the number of members in the multixact.	We determine the latter as	 * the difference between this multixact's starting offset and the next	 * one's.  However, there are some corner cases to worry about:	 *	 * 1. This multixact may be the latest one created, in which case there is	 * no next one to look at.	In this case the nextOffset value we just	 * saved is the correct endpoint.	 *	 * 2. The next multixact may still be in process of being filled in: that	 * is, another process may have done GetNewMultiXactId but not yet written	 * the offset entry for that ID.  In that scenario, it is guaranteed that	 * the offset entry for that multixact exists (because GetNewMultiXactId	 * won't release MultiXactGenLock until it does) but contains zero	 * (because we are careful to pre-zero offset pages). Because	 * GetNewMultiXactId will never return zero as the starting offset for a	 * multixact, when we read zero as the next multixact's offset, we know we	 * have this case.	We sleep for a bit and try again.	 *	 * 3. Because GetNewMultiXactId increments offset zero to offset one to	 * handle case #2, there is an ambiguity near the point of offset	 * wraparound.	If we see next multixact's offset is one, is that our	 * multixact's actual endpoint, or did it end at zero with a subsequent	 * increment?  We handle this using the knowledge that if the zero'th	 * member slot wasn't filled, it'll contain zero, and zero isn't a valid

⌨️ 快捷键说明

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