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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy