Optimize locking a tuple already locked by another subxact

Locking and updating the same tuple repeatedly led to some strange
multixacts being created which had several subtransactions of the same
parent transaction holding locks of the same strength.  However,
once a subxact of the current transaction holds a lock of a given
strength, it's not necessary to acquire the same lock again.  This made
some coding patterns much slower than required.

The fix is twofold.  First we change HeapTupleSatisfiesUpdate to return
HeapTupleBeingUpdated for the case where the current transaction is
already a single-xid locker for the given tuple; it used to return
HeapTupleMayBeUpdated for that case.  The new logic is simpler, and the
change to pgrowlocks is a testament to that: previously we needed to
check for the single-xid locker separately in a very ugly way.  That
test is simpler now.

As fallout from the HTSU change, some of its callers need to be amended
so that tuple-locked-by-own-transaction is taken into account in the
BeingUpdated case rather than the MayBeUpdated case.  For many of them
there is no difference; but heap_delete() and heap_update now check
explicitely and do not grab tuple lock in that case.

The HTSU change also means that routine MultiXactHasRunningRemoteMembers
introduced in commit 11ac4c73cb is no longer necessary and can be
removed; the case that used to require it is now handled naturally as
result of the changes to heap_delete and heap_update.

The second part of the fix to the performance issue is to adjust
heap_lock_tuple to avoid the slowness:

1. Previously we checked for the case that our own transaction already
held a strong enough lock and returned MayBeUpdated, but only in the
multixact case.  Now we do it for the plain Xid case as well, which
saves having to LockTuple.

2. If the current transaction is the only locker of the tuple (but with
a lock not as strong as what we need; otherwise it would have been
caught in the check mentioned above), we can skip sleeping on the
multixact, and instead go straight to create an updated multixact with
the additional lock strength.

3. Most importantly, make sure that both the single-xid-locker case and
the multixact-locker case optimization are applied always.  We do this
by checking both in a single place, rather than them appearing in two
separate portions of the routine -- something that is made possible by
the HeapTupleSatisfiesUpdate API change.  Previously we would only check
for the single-xid case when HTSU returned MayBeUpdated, and only
checked for the multixact case when HTSU returned BeingUpdated.  This
was at odds with what HTSU actually returned in one case: if our own
transaction was locker in a multixact, it returned MayBeUpdated, so the
optimization never applied.  This is what led to the large multixacts in
the first place.

Per bug report #8470 by Oskari Saarenmaa.
This commit is contained in:
Alvaro Herrera 2015-04-10 13:47:15 -03:00
parent 8a0d34e4e4
commit 27846f02c1
5 changed files with 238 additions and 270 deletions

View File

@ -136,14 +136,9 @@ 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.
* A tuple is locked if HTSU returns BeingUpdated.
*/
if (htsu == HeapTupleBeingUpdated ||
(htsu == HeapTupleMayBeUpdated &&
!(infomask & HEAP_XMAX_INVALID) &&
!(infomask & HEAP_XMAX_IS_MULTI) &&
(xmax == GetCurrentTransactionIdIfAny())))
if (htsu == HeapTupleBeingUpdated)
{
char **values;

View File

@ -2694,42 +2694,47 @@ l1:
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.
* Sleep until concurrent transaction ends -- except when there's a single
* locker and it's our own transaction. Note we don't care
* which lock mode the locker has, because we need the strongest one.
*
* Before sleeping, we need to 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.
*/
heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive,
LockWaitBlock, &have_tuple_lock);
/*
* 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_self), XLTW_Delete,
NULL);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
LockTupleExclusive))
{
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/*
* 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;
/* acquire tuple lock, if necessary */
heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive,
LockWaitBlock, &have_tuple_lock);
/* wait for multixact */
MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask,
relation, &(tp.t_self), 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
@ -2741,9 +2746,15 @@ l1:
* since we are about to overwrite the xmax altogether.
*/
}
else
else if (!TransactionIdIsCurrentTransactionId(xwait))
{
/* wait for regular transaction to end */
/*
* Wait for regular transaction to end; but first, acquire
* tuple lock.
*/
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive,
LockWaitBlock, &have_tuple_lock);
XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
@ -3204,8 +3215,6 @@ l2:
uint16 infomask;
bool can_continue = false;
checked_lockers = true;
/*
* XXX note that we don't consider the "no wait" case here. This
* isn't a problem currently because no caller uses that case, but it
@ -3223,8 +3232,6 @@ l2:
xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
infomask = oldtup.t_data->t_infomask;
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/*
* 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
@ -3252,29 +3259,35 @@ l2:
TransactionId update_xact;
int remain;
/* acquire tuple lock, if necessary */
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask, *lockmode))
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
*lockmode))
{
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/* acquire tuple lock, if necessary */
heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
LockWaitBlock, &have_tuple_lock);
/* wait for multixact */
MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
relation, &oldtup.t_self, XLTW_Update,
&remain);
checked_lockers = true;
locker_remains = remain != 0;
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;
}
/* wait for multixact */
MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
relation, &oldtup.t_self, 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
@ -3294,9 +3307,10 @@ l2:
* 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);
else
update_xact = InvalidTransactionId;
/*
* There was no UPDATE in the MultiXact; or it aborted. No
@ -3306,61 +3320,56 @@ l2:
if (!TransactionIdIsValid(update_xact) ||
TransactionIdDidAbort(update_xact))
can_continue = true;
locker_remains = remain != 0;
}
else if (TransactionIdIsCurrentTransactionId(xwait))
{
/*
* The only locker is ourselves; we can avoid grabbing the tuple
* lock here, but must preserve our locking information.
*/
checked_lockers = true;
locker_remains = true;
can_continue = true;
}
else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
{
/*
* 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.
*/
checked_lockers = true;
locker_remains = true;
can_continue = true;
}
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.
* Wait for regular transaction to end; but first, acquire
* tuple lock.
*/
if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
{
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
LockWaitBlock, &have_tuple_lock);
XactLockTableWait(xwait, relation, &oldtup.t_self,
XLTW_Update);
checked_lockers = true;
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;
/*
* 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;
locker_remains = true;
}
else
{
/*
* Wait for regular transaction to end; but first, acquire
* tuple lock.
*/
heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
LockWaitBlock, &have_tuple_lock);
XactLockTableWait(xwait, relation, &oldtup.t_self,
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;
}
}
result = can_continue ? HeapTupleMayBeUpdated : HeapTupleUpdated;
@ -4097,6 +4106,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
uint16 old_infomask,
new_infomask,
new_infomask2;
bool first_time = true;
bool have_tuple_lock = false;
*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@ -4136,47 +4146,77 @@ l3:
/*
* If any subtransaction of the current top transaction already holds
* a lock as strong or stronger than what we're requesting, we
* 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 (infomask & HEAP_XMAX_IS_MULTI)
if (first_time)
{
int i;
int nmembers;
MultiXactMember *members;
first_time = false;
/*
* 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 (infomask & HEAP_XMAX_IS_MULTI)
{
if (TransactionIdIsCurrentTransactionId(members[i].xid))
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++)
{
LockTupleMode membermode;
/* only consider members of our own transaction */
if (!TransactionIdIsCurrentTransactionId(members[i].xid))
continue;
membermode = TUPLOCK_from_mxstatus(members[i].status);
if (membermode >= mode)
if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
{
if (have_tuple_lock)
UnlockTupleTuplock(relation, tid, mode);
pfree(members);
return HeapTupleMayBeUpdated;
}
}
}
if (members)
pfree(members);
if (members)
pfree(members);
}
else if (TransactionIdIsCurrentTransactionId(xwait))
{
switch (mode)
{
case LockTupleKeyShare:
Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
HEAP_XMAX_IS_EXCL_LOCKED(infomask));
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;
}
}
}
/*
@ -4322,6 +4362,31 @@ l3:
}
}
/*
* As a check independent from those above, we can also avoid sleeping
* if the current transaction is the sole locker of the tuple. Note
* that the strength of the lock already held is irrelevant; this is
* not about recording the lock in Xmax (which will be done regardless
* of this optimization, below). Also, note that the cases where we
* hold a lock stronger than we are requesting are already handled
* above by not doing anything.
*
* Note we only deal with the non-multixact case here; MultiXactIdWait
* is well equipped to deal with this situation on its own.
*/
if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
TransactionIdIsCurrentTransactionId(xwait))
{
/* ... but 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;
Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
require_sleep = false;
}
/*
* By here, we either have already acquired the buffer exclusive lock,
* or we must wait for the locking transaction or multixact; so below
@ -4331,9 +4396,9 @@ l3:
if (require_sleep)
{
/*
* Acquire tuple lock to establish our priority for the tuple.
* LockTuple will release us when we are next-in-line for the tuple.
* We must do this even if we are share-locking.
* Acquire tuple lock to establish our priority for the tuple, or
* die trying. LockTuple will release us when we are next-in-line
* for the tuple. We must do this even if we are share-locking.
*
* 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
@ -4390,37 +4455,6 @@ l3:
break;
}
/* if there are updates, follow the update chain */
if (follow_updates &&
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
{
HTSU_Result res;
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
GetCurrentTransactionId(),
mode);
if (res != HeapTupleMayBeUpdated)
{
result = res;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
goto failed;
}
}
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(tuple->t_data->t_infomask, infomask) ||
!TransactionIdEquals(
HeapTupleHeaderGetRawXmax(tuple->t_data),
xwait))
goto l3;
/*
* Of course, the multixact might not be done here: if we're
* requesting a light lock mode, other transactions with light
@ -4457,43 +4491,46 @@ l3:
RelationGetRelationName(relation))));
break;
}
}
/* if there are updates, follow the update chain */
if (follow_updates &&
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
/* if there are updates, follow the update chain */
if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
{
HTSU_Result res;
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
GetCurrentTransactionId(),
mode);
if (res != HeapTupleMayBeUpdated)
{
HTSU_Result res;
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
GetCurrentTransactionId(),
mode);
if (res != HeapTupleMayBeUpdated)
{
result = res;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
goto failed;
}
result = res;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
goto failed;
}
}
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
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(tuple->t_data->t_infomask, infomask) ||
!TransactionIdEquals(
HeapTupleHeaderGetRawXmax(tuple->t_data),
xwait))
goto l3;
/*
* 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(tuple->t_data->t_infomask, infomask) ||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
xwait))
goto l3;
if (!(infomask & HEAP_XMAX_IS_MULTI))
{
/*
* Otherwise check if it committed or aborted. Note we cannot
* be here if the tuple was only locked by somebody who didn't
* conflict with us; that should have been handled above. So
* that transaction must necessarily be gone by now.
* conflict with us; that would have been handled above. So
* that transaction must necessarily be gone by now. But don't
* check for this in the multixact case, because some locker
* transactions might still be running.
*/
UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
}
@ -4536,37 +4573,6 @@ failed:
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
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
@ -4829,7 +4835,7 @@ l5:
if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
TransactionIdDidAbort(MultiXactIdGetUpdateXid(xmax,
!TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
old_infomask)))
{
/*

View File

@ -533,7 +533,7 @@ MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
*/
nmembers = GetMultiXactIdMembers(multi, &members, false, isLockOnly);
if (nmembers < 0)
if (nmembers <= 0)
{
debug_elog2(DEBUG2, "IsRunning: no members");
return false;
@ -1337,39 +1337,6 @@ retry:
return truelength;
}
/*
* 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

View File

@ -514,20 +514,20 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
if (MultiXactHasRunningRemoteMembers(xmax))
if (MultiXactIdIsRunning(xmax, true))
return HeapTupleBeingUpdated;
else
return HeapTupleMayBeUpdated;
}
/* if locker is gone, all's well */
/*
* 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;
if (!TransactionIdIsCurrentTransactionId(xmax))
return HeapTupleBeingUpdated;
else
return HeapTupleMayBeUpdated;
return HeapTupleBeingUpdated;
}
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
@ -539,10 +539,11 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
/* updating subtransaction must have aborted */
/* deleting subtransaction must have aborted */
if (!TransactionIdIsCurrentTransactionId(xmax))
{
if (MultiXactHasRunningRemoteMembers(HeapTupleHeaderGetRawXmax(tuple)))
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
false))
return HeapTupleBeingUpdated;
return HeapTupleMayBeUpdated;
}
@ -664,7 +665,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return HeapTupleMayBeUpdated;
return HeapTupleBeingUpdated;
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
return HeapTupleSelfUpdated; /* updated after scan started */
else

View File

@ -96,7 +96,6 @@ 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);