Skip to content

Commit 4c71f48

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 2a4b6ee commit 4c71f48

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
@@ -4777,6 +4777,83 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
47774777
*result_xmax = new_xmax;
47784778
}
47794779

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

47814858
/*
47824859
* Recursive part of heap_lock_updated_tuple
@@ -4794,7 +4871,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
47944871
Buffer buf;
47954872
uint16 new_infomask,
47964873
new_infomask2,
4797-
old_infomask;
4874+
old_infomask,
4875+
old_infomask2;
47984876
TransactionId xmax,
47994877
new_xmax;
48004878
TransactionId priorXmax = InvalidTransactionId;
@@ -4836,45 +4914,107 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
48364914
}
48374915

48384916
old_infomask = mytup.t_data->t_infomask;
4917+
old_infomask2 = mytup.t_data->t_infomask2;
48394918
xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
48404919

48414920
/*
4842-
* If this tuple is updated and the key has been modified (or
4843-
* deleted), what we do depends on the status of the updating
4844-
* transaction: if it's live, we sleep until it finishes; if it has
4845-
* committed, we have to fail (i.e. return HeapTupleUpdated); if it
4846-
* aborted, we ignore it. For updates that didn't touch the key, we
4847-
* can just plough ahead.
4921+
* If this tuple version has been updated or locked by some concurrent
4922+
* transaction(s), what we do depends on whether our lock mode
4923+
* conflicts with what those other transactions hold, and also on the
4924+
* status of them.
48484925
*/
4849-
if (!(old_infomask & HEAP_XMAX_INVALID) &&
4850-
(mytup.t_data->t_infomask2 & HEAP_KEYS_UPDATED))
4926+
if (!(old_infomask & HEAP_XMAX_INVALID))
48514927
{
4852-
TransactionId update_xid;
4928+
TransactionId rawxmax;
4929+
bool needwait;
48534930

4854-
/*
4855-
* Note: we *must* check TransactionIdIsInProgress before
4856-
* TransactionIdDidAbort/Commit; see comment at top of tqual.c for
4857-
* an explanation.
4858-
*/
4859-
update_xid = HeapTupleHeaderGetUpdateXid(mytup.t_data);
4860-
if (TransactionIdIsCurrentTransactionId(update_xid))
4931+
rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
4932+
if (old_infomask & HEAP_XMAX_IS_MULTI)
48614933
{
4862-
UnlockReleaseBuffer(buf);
4863-
return HeapTupleSelfUpdated;
4864-
}
4865-
else if (TransactionIdIsInProgress(update_xid))
4866-
{
4867-
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
4868-
/* No LockTupleTuplock here -- see heap_lock_updated_tuple */
4869-
XactLockTableWait(update_xid);
4870-
goto l4;
4934+
int nmembers;
4935+
int i;
4936+
MultiXactMember *members;
4937+
4938+
nmembers = GetMultiXactIdMembers(rawxmax, &members, false);
4939+
for (i = 0; i < nmembers; i++)
4940+
{
4941+
HTSU_Result res;
4942+
4943+
res = test_lockmode_for_conflict(members[i].status,
4944+
members[i].xid,
4945+
mode, &needwait);
4946+
4947+
if (needwait)
4948+
{
4949+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
4950+
XactLockTableWait(members[i].xid);
4951+
pfree(members);
4952+
goto l4;
4953+
}
4954+
if (res != HeapTupleMayBeUpdated)
4955+
{
4956+
UnlockReleaseBuffer(buf);
4957+
pfree(members);
4958+
return res;
4959+
}
4960+
}
4961+
if (members)
4962+
pfree(members);
48714963
}
4872-
else if (TransactionIdDidAbort(update_xid))
4873-
; /* okay to proceed */
4874-
else if (TransactionIdDidCommit(update_xid))
4964+
else
48754965
{
4876-
UnlockReleaseBuffer(buf);
4877-
return HeapTupleUpdated;
4966+
HTSU_Result res;
4967+
MultiXactStatus status;
4968+
4969+
/*
4970+
* For a non-multi Xmax, we first need to compute the
4971+
* corresponding MultiXactStatus by using the infomask bits.
4972+
*/
4973+
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
4974+
{
4975+
if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
4976+
status = MultiXactStatusForKeyShare;
4977+
else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
4978+
status = MultiXactStatusForShare;
4979+
else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
4980+
{
4981+
if (old_infomask2 & HEAP_KEYS_UPDATED)
4982+
status = MultiXactStatusForUpdate;
4983+
else
4984+
status = MultiXactStatusForNoKeyUpdate;
4985+
}
4986+
else
4987+
{
4988+
/*
4989+
* LOCK_ONLY present alone (a pg_upgraded tuple
4990+
* marked as share-locked in the old cluster) shouldn't
4991+
* be seen in the middle of an update chain.
4992+
*/
4993+
elog(ERROR, "invalid lock status in tuple");
4994+
}
4995+
}
4996+
else
4997+
{
4998+
/* it's an update, but which kind? */
4999+
if (old_infomask2 & HEAP_KEYS_UPDATED)
5000+
status = MultiXactStatusUpdate;
5001+
else
5002+
status = MultiXactStatusNoKeyUpdate;
5003+
}
5004+
5005+
res = test_lockmode_for_conflict(status, rawxmax, mode,
5006+
&needwait);
5007+
if (needwait)
5008+
{
5009+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
5010+
XactLockTableWait(rawxmax);
5011+
goto l4;
5012+
}
5013+
if (res != HeapTupleMayBeUpdated)
5014+
{
5015+
UnlockReleaseBuffer(buf);
5016+
return res;
5017+
}
48785018
}
48795019
}
48805020

0 commit comments

Comments
 (0)