Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 247c76a

Browse files
committed
Use a more granular approach to follow update chains
Instead of simply checking the KEYS_UPDATED bit, we need to check whether each lock held on the future version of the tuple conflicts with the lock we're trying to acquire. Per bug report #8434 by Tomonari Katsumata
1 parent e4828e9 commit 247c76a

File tree

1 file changed

+171
-31
lines changed

1 file changed

+171
-31
lines changed

src/backend/access/heap/heapam.c

Lines changed: 171 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4794,6 +4794,83 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
47944794
*result_xmax = new_xmax;
47954795
}
47964796

4797+
/*
4798+
* Subroutine for heap_lock_updated_tuple_rec.
4799+
*
4800+
* Given an hypothetical multixact status held by the transaction identified
4801+
* with the given xid, does the current transaction need to wait, fail, or can
4802+
* it continue if it wanted to acquire a lock of the given mode? "needwait"
4803+
* is set to true if waiting is necessary; if it can continue, then
4804+
* HeapTupleMayBeUpdated is returned. In case of a conflict, a different
4805+
* HeapTupleSatisfiesUpdate return code is returned.
4806+
*
4807+
* The held status is said to be hypothetical because it might correspond to a
4808+
* lock held by a single Xid, i.e. not a real MultiXactId; we express it this
4809+
* way for simplicity of API.
4810+
*/
4811+
static HTSU_Result
4812+
test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
4813+
LockTupleMode mode, bool *needwait)
4814+
{
4815+
MultiXactStatus wantedstatus;
4816+
4817+
*needwait = false;
4818+
wantedstatus = get_mxact_status_for_lock(mode, false);
4819+
4820+
/*
4821+
* Note: we *must* check TransactionIdIsInProgress before
4822+
* TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
4823+
* explanation.
4824+
*/
4825+
if (TransactionIdIsCurrentTransactionId(xid))
4826+
{
4827+
/*
4828+
* Updated by our own transaction? Just return failure. This shouldn't
4829+
* normally happen.
4830+
*/
4831+
return HeapTupleSelfUpdated;
4832+
}
4833+
else if (TransactionIdIsInProgress(xid))
4834+
{
4835+
/*
4836+
* If the locking transaction is running, what we do depends on whether
4837+
* the lock modes conflict: if they do, then we must wait for it to
4838+
* finish; otherwise we can fall through to lock this tuple version
4839+
* without waiting.
4840+
*/
4841+
if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
4842+
LOCKMODE_from_mxstatus(wantedstatus)))
4843+
{
4844+
*needwait = true;
4845+
}
4846+
4847+
/*
4848+
* If we set needwait above, then this value doesn't matter; otherwise,
4849+
* this value signals to caller that it's okay to proceed.
4850+
*/
4851+
return HeapTupleMayBeUpdated;
4852+
}
4853+
else if (TransactionIdDidAbort(xid))
4854+
return HeapTupleMayBeUpdated;
4855+
else if (TransactionIdDidCommit(xid))
4856+
{
4857+
/*
4858+
* If the updating transaction committed, what we do depends on whether
4859+
* the lock modes conflict: if they do, then we must report error to
4860+
* caller. But if they don't, we can fall through to lock it.
4861+
*/
4862+
if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
4863+
LOCKMODE_from_mxstatus(wantedstatus)))
4864+
/* bummer */
4865+
return HeapTupleUpdated;
4866+
4867+
return HeapTupleMayBeUpdated;
4868+
}
4869+
4870+
/* Not in progress, not aborted, not committed -- must have crashed */
4871+
return HeapTupleMayBeUpdated;
4872+
}
4873+
47974874

47984875
/*
47994876
* Recursive part of heap_lock_updated_tuple
@@ -4811,7 +4888,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
48114888
Buffer buf;
48124889
uint16 new_infomask,
48134890
new_infomask2,
4814-
old_infomask;
4891+
old_infomask,
4892+
old_infomask2;
48154893
TransactionId xmax,
48164894
new_xmax;
48174895
TransactionId priorXmax = InvalidTransactionId;
@@ -4853,45 +4931,107 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
48534931
}
48544932

48554933
old_infomask = mytup.t_data->t_infomask;
4934+
old_infomask2 = mytup.t_data->t_infomask2;
48564935
xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
48574936

48584937
/*
4859-
* If this tuple is updated and the key has been modified (or
4860-
* deleted), what we do depends on the status of the updating
4861-
* transaction: if it's live, we sleep until it finishes; if it has
4862-
* committed, we have to fail (i.e. return HeapTupleUpdated); if it
4863-
* aborted, we ignore it. For updates that didn't touch the key, we
4864-
* can just plough ahead.
4938+
* If this tuple version has been updated or locked by some concurrent
4939+
* transaction(s), what we do depends on whether our lock mode
4940+
* conflicts with what those other transactions hold, and also on the
4941+
* status of them.
48654942
*/
4866-
if (!(old_infomask & HEAP_XMAX_INVALID) &&
4867-
(mytup.t_data->t_infomask2 & HEAP_KEYS_UPDATED))
4943+
if (!(old_infomask & HEAP_XMAX_INVALID))
48684944
{
4869-
TransactionId update_xid;
4945+
TransactionId rawxmax;
4946+
bool needwait;
48704947

4871-
/*
4872-
* Note: we *must* check TransactionIdIsInProgress before
4873-
* TransactionIdDidAbort/Commit; see comment at top of tqual.c for
4874-
* an explanation.
4875-
*/
4876-
update_xid = HeapTupleHeaderGetUpdateXid(mytup.t_data);
4877-
if (TransactionIdIsCurrentTransactionId(update_xid))
4948+
rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
4949+
if (old_infomask & HEAP_XMAX_IS_MULTI)
48784950
{
4879-
UnlockReleaseBuffer(buf);
4880-
return HeapTupleSelfUpdated;
4881-
}
4882-
else if (TransactionIdIsInProgress(update_xid))
4883-
{
4884-
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
4885-
/* No LockTupleTuplock here -- see heap_lock_updated_tuple */
4886-
XactLockTableWait(update_xid);
4887-
goto l4;
4951+
int nmembers;
4952+
int i;
4953+
MultiXactMember *members;
4954+
4955+
nmembers = GetMultiXactIdMembers(rawxmax, &members, false, false);
4956+
for (i = 0; i < nmembers; i++)
4957+
{
4958+
HTSU_Result res;
4959+
4960+
res = test_lockmode_for_conflict(members[i].status,
4961+
members[i].xid,
4962+
mode, &needwait);
4963+
4964+
if (needwait)
4965+
{
4966+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
4967+
XactLockTableWait(members[i].xid);
4968+
pfree(members);
4969+
goto l4;
4970+
}
4971+
if (res != HeapTupleMayBeUpdated)
4972+
{
4973+
UnlockReleaseBuffer(buf);
4974+
pfree(members);
4975+
return res;
4976+
}
4977+
}
4978+
if (members)
4979+
pfree(members);
48884980
}
4889-
else if (TransactionIdDidAbort(update_xid))
4890-
; /* okay to proceed */
4891-
else if (TransactionIdDidCommit(update_xid))
4981+
else
48924982
{
4893-
UnlockReleaseBuffer(buf);
4894-
return HeapTupleUpdated;
4983+
HTSU_Result res;
4984+
MultiXactStatus status;
4985+
4986+
/*
4987+
* For a non-multi Xmax, we first need to compute the
4988+
* corresponding MultiXactStatus by using the infomask bits.
4989+
*/
4990+
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
4991+
{
4992+
if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
4993+
status = MultiXactStatusForKeyShare;
4994+
else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
4995+
status = MultiXactStatusForShare;
4996+
else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
4997+
{
4998+
if (old_infomask2 & HEAP_KEYS_UPDATED)
4999+
status = MultiXactStatusForUpdate;
5000+
else
5001+
status = MultiXactStatusForNoKeyUpdate;
5002+
}
5003+
else
5004+
{
5005+
/*
5006+
* LOCK_ONLY present alone (a pg_upgraded tuple
5007+
* marked as share-locked in the old cluster) shouldn't
5008+
* be seen in the middle of an update chain.
5009+
*/
5010+
elog(ERROR, "invalid lock status in tuple");
5011+
}
5012+
}
5013+
else
5014+
{
5015+
/* it's an update, but which kind? */
5016+
if (old_infomask2 & HEAP_KEYS_UPDATED)
5017+
status = MultiXactStatusUpdate;
5018+
else
5019+
status = MultiXactStatusNoKeyUpdate;
5020+
}
5021+
5022+
res = test_lockmode_for_conflict(status, rawxmax, mode,
5023+
&needwait);
5024+
if (needwait)
5025+
{
5026+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
5027+
XactLockTableWait(rawxmax);
5028+
goto l4;
5029+
}
5030+
if (res != HeapTupleMayBeUpdated)
5031+
{
5032+
UnlockReleaseBuffer(buf);
5033+
return res;
5034+
}
48955035
}
48965036
}
48975037

0 commit comments

Comments
 (0)