I have just pushed two optimization patches for multixacts to HEAD only.
I hesitate to backpatch them to 9.3 right away, but will consider doing
so if people think differently.

I also have a patch that supposedly fixes the performance regression
reported in bug #8470, but it's considerably more obscure so I'm not
pushing it right now.  It's attached.  I'd need to spend some more time
with it to ensure it doesn't break other cases before pushing.  I know
some people is interested in this fix and thought I'd throw it out there
to gather some input.

Since it affects much of the same code as the two patches I just pushed,
using it in 9.3 requires cherry-picking those patches, but they apply
without conflicts so it should be easy.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
*** a/contrib/pgrowlocks/pgrowlocks.c
--- b/contrib/pgrowlocks/pgrowlocks.c
***************
*** 136,149 **** pgrowlocks(PG_FUNCTION_ARGS)
  		infomask = tuple->t_data->t_infomask;
  
  		/*
! 		 * a tuple is locked if HTSU returns BeingUpdated, and if it returns
! 		 * MayBeUpdated but the Xmax is valid and pointing at us.
  		 */
! 		if (htsu == HeapTupleBeingUpdated ||
! 			(htsu == HeapTupleMayBeUpdated &&
! 			 !(infomask & HEAP_XMAX_INVALID) &&
! 			 !(infomask & HEAP_XMAX_IS_MULTI) &&
! 			 (xmax == GetCurrentTransactionIdIfAny())))
  		{
  			char	  **values;
  
--- 136,144 ----
  		infomask = tuple->t_data->t_infomask;
  
  		/*
! 		 * A tuple is locked if HTSU returns BeingUpdated.
  		 */
! 		if (htsu == HeapTupleBeingUpdated)
  		{
  			char	  **values;
  
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 2711,2791 **** l1:
  	}
  	else if (result == HeapTupleBeingUpdated && wait)
  	{
- 		TransactionId xwait;
- 		uint16		infomask;
- 
- 		/* must copy state data before unlocking buffer */
- 		xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
- 		infomask = tp.t_data->t_infomask;
- 
- 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- 
  		/*
! 		 * Acquire tuple lock to establish our priority for the tuple (see
! 		 * heap_lock_tuple).  LockTuple will release us when we are
! 		 * next-in-line for the tuple.
  		 *
! 		 * If we are forced to "start over" below, we keep the tuple lock;
! 		 * this arranges that we stay at the head of the line while rechecking
! 		 * tuple state.
  		 */
! 		if (!have_tuple_lock)
  		{
! 			LockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
! 			have_tuple_lock = true;
  		}
  
! 		/*
! 		 * Sleep until concurrent transaction ends.  Note that we don't care
! 		 * which lock mode the locker has, because we need the strongest one.
! 		 */
  
! 		if (infomask & HEAP_XMAX_IS_MULTI)
! 		{
! 			/* wait for multixact */
! 			MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask,
! 							relation, &tp.t_data->t_ctid, XLTW_Delete,
! 							NULL);
! 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
  			/*
! 			 * If xwait had just locked the tuple then some other xact could
! 			 * update this tuple before we get to this point.  Check for xmax
! 			 * change, and start over if so.
  			 */
! 			if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
! 				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
! 									 xwait))
! 				goto l1;
  
  			/*
! 			 * You might think the multixact is necessarily done here, but not
! 			 * so: it could have surviving members, namely our own xact or
! 			 * other subxacts of this backend.  It is legal for us to delete
! 			 * the tuple in either case, however (the latter case is
! 			 * essentially a situation of upgrading our former shared lock to
! 			 * exclusive).  We don't bother changing the on-disk hint bits
! 			 * since we are about to overwrite the xmax altogether.
  			 */
- 		}
- 		else
- 		{
- 			/* wait for regular transaction to end */
- 			XactLockTableWait(xwait, relation, &tp.t_data->t_ctid, XLTW_Delete);
- 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
! 			/*
! 			 * xwait is done, but if xwait had just locked the tuple then some
! 			 * other xact could update this tuple before we get to this point.
! 			 * Check for xmax change, and start over if so.
! 			 */
! 			if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
! 				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
! 									 xwait))
! 				goto l1;
  
! 			/* Otherwise check if it committed or aborted */
! 			UpdateXmaxHintBits(tp.t_data, buffer, xwait);
  		}
  
  		/*
--- 2711,2814 ----
  	}
  	else if (result == HeapTupleBeingUpdated && wait)
  	{
  		/*
! 		 * Somebody is holding a lock on the tuple, or updating it; we now
! 		 * need to sleep on that transaction before we can proceed.  However,
! 		 * if the only locker is our own transaction (or any subtransaction of
! 		 * the current top transaction), then it's not necessary to do so.
! 		 * Note we only check for this case when the locker is a single xid,
! 		 * because MultiXactIdWait is prepared to deal with it.
  		 *
! 		 * This must be done before acquiring our tuple lock, to avoid
! 		 * deadlocks with other transactions that are already waiting on the
! 		 * lock we hold.
  		 */
! 		if (!(tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI) &&
! 			TransactionIdIsCurrentTransactionId(
! 									   HeapTupleHeaderGetRawXmax(tp.t_data)))
  		{
! 			Assert(HEAP_XMAX_IS_LOCKED_ONLY(tp.t_data->t_infomask));
  		}
+ 		else
+ 		{
+ 			TransactionId xwait;
+ 			uint16		infomask;
  
! 			/* must copy state data before unlocking buffer */
! 			xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
! 			infomask = tp.t_data->t_infomask;
  
! 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  
  			/*
! 			 * Acquire tuple lock to establish our priority for the tuple (see
! 			 * heap_lock_tuple).  LockTuple will release us when we are
! 			 * next-in-line for the tuple.
! 			 *
! 			 * If we are forced to "start over" below, we keep the tuple lock;
! 			 * this arranges that we stay at the head of the line while
! 			 * rechecking tuple state.
  			 */
! 			if (!have_tuple_lock)
! 			{
! 				LockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
! 				have_tuple_lock = true;
! 			}
  
  			/*
! 			 * Sleep until concurrent transaction ends.  Note that we don't
! 			 * care which lock mode the locker has, because we need the
! 			 * strongest one.
  			 */
  
! 			if (infomask & HEAP_XMAX_IS_MULTI)
! 			{
! 				/* wait for multixact */
! 				MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask,
! 								relation, &tp.t_data->t_ctid, XLTW_Delete,
! 								NULL);
! 				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
! 
! 				/*
! 				 * If xwait had just locked the tuple then some other xact
! 				 * could update this tuple before we get to this point.  Check
! 				 * for xmax change, and start over if so.
! 				 */
! 				if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
! 					!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
! 										 xwait))
! 					goto l1;
! 
! 				/*
! 				 * You might think the multixact is necessarily done here, but
! 				 * not so: it could have surviving members, namely our own
! 				 * xact or other subxacts of this backend.  It is legal for us
! 				 * to delete the tuple in either case, however (the latter
! 				 * case is essentially a situation of upgrading our former
! 				 * shared lock to exclusive).  We don't bother changing the
! 				 * on-disk hint bits since we are about to overwrite the xmax
! 				 * altogether.
! 				 */
! 			}
! 			else
! 			{
! 				/* wait for regular transaction to end */
! 				XactLockTableWait(xwait, relation, &tp.t_data->t_ctid, XLTW_Delete);
! 				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
! 				/*
! 				 * xwait is done, but if xwait had just locked the tuple then
! 				 * some other xact could update this tuple before we get to
! 				 * this point. Check for xmax change, and start over if so.
! 				 */
! 				if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
! 					!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
! 										 xwait))
! 					goto l1;
! 
! 				/* Otherwise check if it committed or aborted */
! 				UpdateXmaxHintBits(tp.t_data, buffer, xwait);
! 			}
  		}
  
  		/*
***************
*** 3239,3246 **** l2:
  	}
  	else if (result == HeapTupleBeingUpdated && wait)
  	{
- 		TransactionId xwait;
- 		uint16		infomask;
  		bool		can_continue = false;
  
  		checked_lockers = true;
--- 3262,3267 ----
***************
*** 3258,3396 **** l2:
  		 * heap_update directly.
  		 */
  
- 		/* must copy state data before unlocking buffer */
- 		xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
- 		infomask = oldtup.t_data->t_infomask;
- 
- 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- 
  		/*
! 		 * Acquire tuple lock to establish our priority for the tuple (see
! 		 * heap_lock_tuple).  LockTuple will release us when we are
! 		 * next-in-line for the tuple.
  		 *
! 		 * If we are forced to "start over" below, we keep the tuple lock;
! 		 * this arranges that we stay at the head of the line while rechecking
! 		 * tuple state.
  		 */
! 		if (!have_tuple_lock)
  		{
! 			LockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
! 			have_tuple_lock = true;
! 		}
  
! 		/*
! 		 * Now we have to do something about the existing locker.  If it's a
! 		 * multi, sleep on it; we might be awakened before it is completely
! 		 * gone (or even not sleep at all in some cases); we need to preserve
! 		 * it as locker, unless it is gone completely.
! 		 *
! 		 * If it's not a multi, we need to check for sleeping conditions
! 		 * before actually going to sleep.  If the update doesn't conflict
! 		 * with the locks, we just continue without sleeping (but making sure
! 		 * it is preserved).
! 		 */
! 		if (infomask & HEAP_XMAX_IS_MULTI)
  		{
! 			TransactionId update_xact;
! 			int			remain;
  
! 			/* wait for multixact */
! 			MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
! 							relation, &oldtup.t_data->t_ctid, XLTW_Update,
! 							&remain);
! 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
! 			/*
! 			 * If xwait had just locked the tuple then some other xact could
! 			 * update this tuple before we get to this point.  Check for xmax
! 			 * change, and start over if so.
! 			 */
! 			if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
! 				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
! 									 xwait))
! 				goto l2;
  
  			/*
! 			 * Note that the multixact may not be done by now.  It could have
! 			 * surviving members; our own xact or other subxacts of this
! 			 * backend, and also any other concurrent transaction that locked
! 			 * the tuple with KeyShare if we only got TupleLockUpdate.  If
! 			 * this is the case, we have to be careful to mark the updated
! 			 * tuple with the surviving members in Xmax.
  			 *
! 			 * Note that there could have been another update in the
! 			 * MultiXact. In that case, we need to check whether it committed
! 			 * or aborted. If it aborted we are safe to update it again;
! 			 * otherwise there is an update conflict, and we have to return
! 			 * HeapTupleUpdated below.
! 			 *
! 			 * In the LockTupleExclusive case, we still need to preserve the
! 			 * surviving members: those would include the tuple locks we had
! 			 * before this one, which are important to keep in case this
! 			 * subxact aborts.
  			 */
! 			update_xact = InvalidTransactionId;
! 			if (!HEAP_XMAX_IS_LOCKED_ONLY(oldtup.t_data->t_infomask))
! 				update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
  
  			/*
! 			 * There was no UPDATE in the MultiXact; or it aborted. No
! 			 * TransactionIdIsInProgress() call needed here, since we called
! 			 * MultiXactIdWait() above.
! 			 */
! 			if (!TransactionIdIsValid(update_xact) ||
! 				TransactionIdDidAbort(update_xact))
! 				can_continue = true;
! 
! 			locker_remains = remain != 0;
! 		}
! 		else
! 		{
! 			/*
! 			 * If it's just a key-share locker, and we're not changing the key
! 			 * columns, we don't need to wait for it to end; but we need to
! 			 * preserve it as locker.
  			 */
! 			if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
  			{
  				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
  				/*
! 				 * recheck the locker; if someone else changed the tuple while
! 				 * we weren't looking, start over.
  				 */
  				if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
! 					!TransactionIdEquals(
! 									HeapTupleHeaderGetRawXmax(oldtup.t_data),
! 										 xwait))
  					goto l2;
  
! 				can_continue = true;
! 				locker_remains = true;
  			}
  			else
  			{
- 				/* wait for regular transaction to end */
- 				XactLockTableWait(xwait, relation, &oldtup.t_data->t_ctid,
- 								  XLTW_Update);
- 				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- 
  				/*
! 				 * xwait is done, but if xwait had just locked the tuple then
! 				 * some other xact could update this tuple before we get to
! 				 * this point. Check for xmax change, and start over if so.
  				 */
! 				if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
! 					!TransactionIdEquals(
! 									HeapTupleHeaderGetRawXmax(oldtup.t_data),
! 										 xwait))
! 					goto l2;
  
- 				/* Otherwise check if it committed or aborted */
- 				UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
- 				if (oldtup.t_data->t_infomask & HEAP_XMAX_INVALID)
  					can_continue = true;
  			}
  		}
  
--- 3279,3444 ----
  		 * heap_update directly.
  		 */
  
  		/*
! 		 * Somebody is holding a lock on the tuple, or updating it; we now
! 		 * need to sleep on that transaction before we can proceed.  However,
! 		 * if the only locker is our own transaction (or any subtransaction of
! 		 * the current top transaction), then it's not necessary to do so.
! 		 * Note we only check for this case when the locker is a single xid,
! 		 * because MultiXactIdWait is prepared to deal with it.
  		 *
! 		 * This must be done before acquiring our tuple lock, to avoid
! 		 * deadlocks with other transactions that are already waiting on the
! 		 * lock we hold.
  		 */
! 		if (!(oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) &&
! 			TransactionIdIsCurrentTransactionId(
! 								   HeapTupleHeaderGetRawXmax(oldtup.t_data)))
  		{
! 			Assert(HEAP_XMAX_IS_LOCKED_ONLY(oldtup.t_data->t_infomask));
  
! 			can_continue = true;
! 			locker_remains = true;
! 		}
! 		else
  		{
! 			TransactionId xwait;
! 			uint16		infomask;
  
! 			/* must copy state data before unlocking buffer */
! 			xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
! 			infomask = oldtup.t_data->t_infomask;
  
! 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  
  			/*
! 			 * Acquire tuple lock to establish our priority for the tuple (see
! 			 * heap_lock_tuple).  LockTuple will release us when we are
! 			 * next-in-line for the tuple.
  			 *
! 			 * If we are forced to "start over" below, we keep the tuple lock;
! 			 * this arranges that we stay at the head of the line while
! 			 * rechecking tuple state.
  			 */
! 			if (!have_tuple_lock)
! 			{
! 				LockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
! 				have_tuple_lock = true;
! 			}
  
  			/*
! 			 * Now we have to do something about the existing locker.  If it's
! 			 * a multi, sleep on it; we might be awakened before it is
! 			 * completely gone (or even not sleep at all in some cases); we
! 			 * need to preserve it as locker, unless it is gone completely.
! 			 *
! 			 * If it's not a multi, we need to check for sleeping conditions
! 			 * before actually going to sleep.  If the update doesn't conflict
! 			 * with the locks, we just continue without sleeping (but making
! 			 * sure it is preserved).
  			 */
! 			if (infomask & HEAP_XMAX_IS_MULTI)
  			{
+ 				TransactionId update_xact;
+ 				int			remain;
+ 
+ 				/* wait for multixact */
+ 				MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
+ 								relation, &oldtup.t_data->t_ctid, XLTW_Update,
+ 								&remain);
  				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  
  				/*
! 				 * If xwait had just locked the tuple then some other xact
! 				 * could update this tuple before we get to this point.  Check
! 				 * for xmax change, and start over if so.
  				 */
  				if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
! 					!TransactionIdEquals(xwait,
! 								   HeapTupleHeaderGetRawXmax(oldtup.t_data)))
  					goto l2;
  
! 				/*
! 				 * Note that the multixact may not be done by now.  It could
! 				 * have surviving members; our own xact or other subxacts of
! 				 * this backend, and also any other concurrent transaction
! 				 * that locked the tuple with KeyShare if we only got
! 				 * TupleLockUpdate.  If this is the case, we have to be
! 				 * careful to mark the updated tuple with the surviving
! 				 * members in Xmax.
! 				 *
! 				 * Note that there could have been another update in the
! 				 * MultiXact. In that case, we need to check whether it
! 				 * committed or aborted. If it aborted we are safe to update
! 				 * it again; otherwise there is an update conflict, and we
! 				 * have to return HeapTupleUpdated below.
! 				 *
! 				 * In the LockTupleExclusive case, we still need to preserve
! 				 * the surviving members: those would include the tuple locks
! 				 * we had before this one, which are important to keep in case
! 				 * this subxact aborts.
! 				 */
! 				update_xact = InvalidTransactionId;
! 				if (!HEAP_XMAX_IS_LOCKED_ONLY(oldtup.t_data->t_infomask))
! 					update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
! 
! 				/*
! 				 * There was no UPDATE in the MultiXact; or it aborted. No
! 				 * TransactionIdIsInProgress() call needed here, since we
! 				 * called MultiXactIdWait() above.
! 				 */
! 				if (!TransactionIdIsValid(update_xact) ||
! 					TransactionIdDidAbort(update_xact))
! 					can_continue = true;
! 
! 				locker_remains = remain != 0;
  			}
  			else
  			{
  				/*
! 				 * If it's just a key-share locker, and we're not changing the
! 				 * key columns, we don't need to wait for it to end; but we
! 				 * need to preserve it as locker.
  				 */
! 				if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
! 				{
! 					LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
! 
! 					/*
! 					 * recheck the locker; if someone else changed the tuple
! 					 * while we weren't looking, start over.
! 					 */
! 					if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
! 						!TransactionIdEquals(xwait,
! 								   HeapTupleHeaderGetRawXmax(oldtup.t_data)))
! 						goto l2;
  
  					can_continue = true;
+ 					locker_remains = true;
+ 				}
+ 				else
+ 				{
+ 					/* wait for regular transaction to end */
+ 					XactLockTableWait(xwait, relation, &oldtup.t_data->t_ctid,
+ 									  XLTW_Update);
+ 					LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 					/*
+ 					 * xwait is done, but if xwait had just locked the tuple
+ 					 * then some other xact could update this tuple before we
+ 					 * get to this point. Check for xmax change, and start
+ 					 * over if so.
+ 					 */
+ 					if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
+ 						!TransactionIdEquals(xwait,
+ 								   HeapTupleHeaderGetRawXmax(oldtup.t_data)))
+ 						goto l2;
+ 
+ 					/* Otherwise check if it committed or aborted */
+ 					UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
+ 					if (oldtup.t_data->t_infomask & HEAP_XMAX_INVALID)
+ 						can_continue = true;
+ 				}
  			}
  		}
  
***************
*** 4167,4213 **** l3:
  
  		/*
  		 * If any subtransaction of the current top transaction already holds
! 		 * a lock as strong or stronger than what we're requesting, we
  		 * effectively hold the desired lock already.  We *must* succeed
  		 * without trying to take the tuple lock, else we will deadlock
  		 * against anyone wanting to acquire a stronger lock.
  		 */
! 		if (infomask & HEAP_XMAX_IS_MULTI)
  		{
! 			int			i;
! 			int			nmembers;
! 			MultiXactMember *members;
  
! 			/*
! 			 * We don't need to allow old multixacts here; if that had been
! 			 * the case, HeapTupleSatisfiesUpdate would have returned
! 			 * MayBeUpdated and we wouldn't be here.
! 			 */
! 			nmembers =
! 				GetMultiXactIdMembers(xwait, &members, false,
! 									  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
  
! 			for (i = 0; i < nmembers; i++)
! 			{
! 				if (TransactionIdIsCurrentTransactionId(members[i].xid))
  				{
! 					LockTupleMode membermode;
  
! 					membermode = TUPLOCK_from_mxstatus(members[i].status);
  
! 					if (membermode >= mode)
! 					{
! 						if (have_tuple_lock)
! 							UnlockTupleTuplock(relation, tid, mode);
  
! 						pfree(members);
  						return HeapTupleMayBeUpdated;
! 					}
  				}
  			}
- 
- 			if (members)
- 				pfree(members);
  		}
  
  		/*
--- 4215,4289 ----
  
  		/*
  		 * If any subtransaction of the current top transaction already holds
! 		 * a lock as strong as or stronger than what we're requesting, we
  		 * effectively hold the desired lock already.  We *must* succeed
  		 * without trying to take the tuple lock, else we will deadlock
  		 * against anyone wanting to acquire a stronger lock.
+ 		 *
+ 		 * Note we only do this the first time we loop on the HTSU result;
+ 		 * there is no point in testing in subsequent passes, because
+ 		 * evidently our own transaction cannot have acquired a new lock after
+ 		 * the first time we checked.
  		 */
! 		if (!have_tuple_lock)
  		{
! 			if (infomask & HEAP_XMAX_IS_MULTI)
! 			{
! 				int			i;
! 				int			nmembers;
! 				MultiXactMember *members;
  
! 				/*
! 				 * We don't need to allow old multixacts here; if that had
! 				 * been the case, HeapTupleSatisfiesUpdate would have returned
! 				 * MayBeUpdated and we wouldn't be here.
! 				 */
! 				nmembers =
! 					GetMultiXactIdMembers(xwait, &members, false,
! 										  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
  
! 				for (i = 0; i < nmembers; i++)
  				{
! 					if (TransactionIdIsCurrentTransactionId(members[i].xid))
! 					{
! 						LockTupleMode membermode;
  
! 						membermode = TUPLOCK_from_mxstatus(members[i].status);
  
! 						if (membermode >= mode)
! 						{
! 							pfree(members);
! 							return HeapTupleMayBeUpdated;
! 						}
! 					}
! 				}
  
! 				if (members)
! 					pfree(members);
! 			}
! 			else if (TransactionIdIsCurrentTransactionId(xwait))
! 			{
! 				switch (mode)
! 				{
! 					case LockTupleKeyShare:
  						return HeapTupleMayBeUpdated;
! 						break;
! 					case LockTupleShare:
! 						if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
! 							HEAP_XMAX_IS_EXCL_LOCKED(infomask))
! 							return HeapTupleMayBeUpdated;
! 						break;
! 					case LockTupleNoKeyExclusive:
! 						if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
! 							return HeapTupleMayBeUpdated;
! 						break;
! 					case LockTupleExclusive:
! 						if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
! 							infomask2 & HEAP_KEYS_UPDATED)
! 							return HeapTupleMayBeUpdated;
! 						break;
  				}
  			}
  		}
  
  		/*
***************
*** 4389,4396 **** l3:
  						 */
  						LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
  						if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
! 							!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
! 												 xwait))
  						{
  							pfree(members);
  							goto l3;
--- 4465,4472 ----
  						 */
  						LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
  						if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
! 							!TransactionIdEquals(xwait,
! 								   HeapTupleHeaderGetRawXmax(tuple->t_data)))
  						{
  							pfree(members);
  							goto l3;
***************
*** 4416,4421 **** l3:
--- 4492,4518 ----
  				require_sleep = false;
  			}
  		}
+ 		else if (!(infomask & HEAP_XMAX_IS_MULTI) &&
+ 				 TransactionIdIsCurrentTransactionId(xwait))
+ 		{
+ 			/*
+ 			 * If the only locker present is ourselves, but we now want a
+ 			 * stronger lock, then we need not sleep (indeed we mustn't,
+ 			 * because XactLockTableWait would error out).  Note that the
+ 			 * cases where we already hold a lock as strong or stronger than
+ 			 * what we already hold were already covered above.  Also note we
+ 			 * don't need to concern ourselves with the multixact case here,
+ 			 * because MultiXactIdWait would have that situation on hand.
+ 			 *
+ 			 * If the xmax changed in the meantime, start over.
+ 			 */
+ 			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ 			if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
+ 				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ 									 xwait))
+ 				goto l3;
+ 			require_sleep = false;
+ 		}
  
  		/*
  		 * By here, we either have already acquired the buffer exclusive lock,
***************
*** 4584,4620 **** failed:
  	old_infomask = tuple->t_data->t_infomask;
  
  	/*
- 	 * We might already hold the desired lock (or stronger), possibly under a
- 	 * different subtransaction of the current top transaction.  If so, there
- 	 * is no need to change state or issue a WAL record.  We already handled
- 	 * the case where this is true for xmax being a MultiXactId, so now check
- 	 * for cases where it is a plain TransactionId.
- 	 *
- 	 * Note in particular that this covers the case where we already hold
- 	 * exclusive lock on the tuple and the caller only wants key share or
- 	 * share lock. It would certainly not do to give up the exclusive lock.
- 	 */
- 	if (!(old_infomask & (HEAP_XMAX_INVALID |
- 						  HEAP_XMAX_COMMITTED |
- 						  HEAP_XMAX_IS_MULTI)) &&
- 		(mode == LockTupleKeyShare ?
- 		 (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_SHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_EXCL_LOCKED(old_infomask)) :
- 		 mode == LockTupleShare ?
- 		 (HEAP_XMAX_IS_SHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_EXCL_LOCKED(old_infomask)) :
- 		 (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))) &&
- 		TransactionIdIsCurrentTransactionId(xmax))
- 	{
- 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- 		/* Probably can't hold tuple lock here, but may as well check */
- 		if (have_tuple_lock)
- 			UnlockTupleTuplock(relation, tid, mode);
- 		return HeapTupleMayBeUpdated;
- 	}
- 
- 	/*
  	 * If this is the first possibly-multixact-able operation in the current
  	 * transaction, set my per-backend OldestMemberMXactId setting. We can be
  	 * certain that the transaction will never become a member of any older
--- 4681,4686 ----
***************
*** 4842,4848 **** l5:
  		if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
  		{
  			if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
! 				TransactionIdDidAbort(MultiXactIdGetUpdateXid(xmax,
  															  old_infomask)))
  			{
  				/*
--- 4908,4914 ----
  		if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
  		{
  			if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
! 				!TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
  															  old_infomask)))
  			{
  				/*
*** a/src/backend/access/transam/multixact.c
--- b/src/backend/access/transam/multixact.c
***************
*** 531,537 **** MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
  	 */
  	nmembers = GetMultiXactIdMembers(multi, &members, false, isLockOnly);
  
! 	if (nmembers < 0)
  	{
  		debug_elog2(DEBUG2, "IsRunning: no members");
  		return false;
--- 531,537 ----
  	 */
  	nmembers = GetMultiXactIdMembers(multi, &members, false, isLockOnly);
  
! 	if (nmembers <= 0)
  	{
  		debug_elog2(DEBUG2, "IsRunning: no members");
  		return false;
***************
*** 1342,1380 **** retry:
  }
  
  /*
-  * MultiXactHasRunningRemoteMembers
-  *		Does the given multixact have still-live members from
-  *		transactions other than our own?
-  */
- bool
- MultiXactHasRunningRemoteMembers(MultiXactId multi)
- {
- 	MultiXactMember *members;
- 	int			nmembers;
- 	int			i;
- 
- 	nmembers = GetMultiXactIdMembers(multi, &members, true, false);
- 	if (nmembers <= 0)
- 		return false;
- 
- 	for (i = 0; i < nmembers; i++)
- 	{
- 		/* not interested in our own members */
- 		if (TransactionIdIsCurrentTransactionId(members[i].xid))
- 			continue;
- 
- 		if (TransactionIdIsInProgress(members[i].xid))
- 		{
- 			pfree(members);
- 			return true;
- 		}
- 	}
- 
- 	pfree(members);
- 	return false;
- }
- 
- /*
   * mxactMemberComparator
   *		qsort comparison function for MultiXactMember
   *
--- 1342,1347 ----
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c
***************
*** 513,532 **** HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
  
  				if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
  				{
! 					if (MultiXactHasRunningRemoteMembers(xmax))
  						return HeapTupleBeingUpdated;
  					else
  						return HeapTupleMayBeUpdated;
  				}
  
! 				/* if locker is gone, all's well */
  				if (!TransactionIdIsInProgress(xmax))
  					return HeapTupleMayBeUpdated;
! 
! 				if (!TransactionIdIsCurrentTransactionId(xmax))
! 					return HeapTupleBeingUpdated;
! 				else
! 					return HeapTupleMayBeUpdated;
  			}
  
  			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
--- 513,532 ----
  
  				if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
  				{
! 					if (MultiXactIdIsRunning(xmax, true))
  						return HeapTupleBeingUpdated;
  					else
  						return HeapTupleMayBeUpdated;
  				}
  
! 				/*
! 				 * If the locker is gone, then there is nothing of interest
! 				 * left in this Xmax; otherwise, report the tuple as
! 				 * locked/updated.
! 				 */
  				if (!TransactionIdIsInProgress(xmax))
  					return HeapTupleMayBeUpdated;
! 				return HeapTupleBeingUpdated;
  			}
  
  			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
***************
*** 538,547 **** HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
  				/* not LOCKED_ONLY, so it has to have an xmax */
  				Assert(TransactionIdIsValid(xmax));
  
! 				/* updating subtransaction must have aborted */
  				if (!TransactionIdIsCurrentTransactionId(xmax))
  				{
! 					if (MultiXactHasRunningRemoteMembers(HeapTupleHeaderGetRawXmax(tuple)))
  						return HeapTupleBeingUpdated;
  					return HeapTupleMayBeUpdated;
  				}
--- 538,548 ----
  				/* not LOCKED_ONLY, so it has to have an xmax */
  				Assert(TransactionIdIsValid(xmax));
  
! 				/* deleting subtransaction must have aborted */
  				if (!TransactionIdIsCurrentTransactionId(xmax))
  				{
! 					if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
! 																	   false))
  						return HeapTupleBeingUpdated;
  					return HeapTupleMayBeUpdated;
  				}
***************
*** 663,669 **** HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
  	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
  	{
  		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
! 			return HeapTupleMayBeUpdated;
  		if (HeapTupleHeaderGetCmax(tuple) >= curcid)
  			return HeapTupleSelfUpdated;		/* updated after scan started */
  		else
--- 664,670 ----
  	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
  	{
  		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
! 			return HeapTupleBeingUpdated;
  		if (HeapTupleHeaderGetCmax(tuple) >= curcid)
  			return HeapTupleSelfUpdated;		/* updated after scan started */
  		else
*** a/src/include/access/multixact.h
--- b/src/include/access/multixact.h
***************
*** 95,101 **** extern bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly);
  extern void MultiXactIdSetOldestMember(void);
  extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
  					  bool allow_old, bool isLockOnly);
- extern bool MultiXactHasRunningRemoteMembers(MultiXactId multi);
  extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
  extern bool MultiXactIdPrecedesOrEquals(MultiXactId multi1,
  							MultiXactId multi2);
--- 95,100 ----
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to