Skip to content

Commit 13aa624

Browse files
committed
Optimize updating a row that's locked by same xid
Updating or locking a row that was already locked by the same transaction under the same Xid caused a MultiXact to be created; but this is unnecessary, because there's no usefulness in being able to differentiate two locks by the same transaction. In particular, if a transaction executed SELECT FOR UPDATE followed by an UPDATE that didn't modify columns of the key, we would dutifully represent the resulting combination as a multixact -- even though a single key-update is sufficient. Optimize the case so that only the strongest of both locks/updates is represented in Xmax. This can save some Xmax's from becoming MultiXacts, which can be a significant optimization. This missed optimization opportunity was spotted by Andres Freund while investigating a bug reported by Oliver Seemann in message CANCipfpfzoYnOz5jj=UZ70_R=CwDHv36dqWSpwsi27vpm1z5sA@mail.gmail.com and also directly as a performance regression reported by Dong Ye in message d54b8387.000012d8.00000010@YED-DEVD1.vmware.com Reportedly, this patch fixes the performance regression. Since the missing optimization was reported as a significant performance regression from 9.2, backpatch to 9.3. Andres Freund, tweaked by Álvaro Herrera
1 parent 084e385 commit 13aa624

File tree

1 file changed

+39
-31
lines changed

1 file changed

+39
-31
lines changed

src/backend/access/heap/heapam.c

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4705,13 +4705,20 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
47054705
uint16 new_infomask,
47064706
new_infomask2;
47074707

4708+
Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
4709+
47084710
l5:
47094711
new_infomask = 0;
47104712
new_infomask2 = 0;
47114713
if (old_infomask & HEAP_XMAX_INVALID)
47124714
{
47134715
/*
47144716
* No previous locker; we just insert our own TransactionId.
4717+
*
4718+
* Note that it's critical that this case be the first one checked,
4719+
* because there are several blocks below that come back to this one
4720+
* to implement certain optimizations; old_infomask might contain
4721+
* other dirty bits in those cases, but we don't really care.
47154722
*/
47164723
if (is_update)
47174724
{
@@ -4837,21 +4844,22 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
48374844
* create a new MultiXactId that includes both the old locker or
48384845
* updater and our own TransactionId.
48394846
*/
4840-
MultiXactStatus status;
48414847
MultiXactStatus new_status;
4848+
MultiXactStatus old_status;
4849+
LockTupleMode old_mode;
48424850

48434851
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
48444852
{
48454853
if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
4846-
status = MultiXactStatusForKeyShare;
4854+
old_status = MultiXactStatusForKeyShare;
48474855
else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
4848-
status = MultiXactStatusForShare;
4856+
old_status = MultiXactStatusForShare;
48494857
else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
48504858
{
48514859
if (old_infomask2 & HEAP_KEYS_UPDATED)
4852-
status = MultiXactStatusForUpdate;
4860+
old_status = MultiXactStatusForUpdate;
48534861
else
4854-
status = MultiXactStatusForNoKeyUpdate;
4862+
old_status = MultiXactStatusForNoKeyUpdate;
48554863
}
48564864
else
48574865
{
@@ -4871,43 +4879,43 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
48714879
{
48724880
/* it's an update, but which kind? */
48734881
if (old_infomask2 & HEAP_KEYS_UPDATED)
4874-
status = MultiXactStatusUpdate;
4882+
old_status = MultiXactStatusUpdate;
48754883
else
4876-
status = MultiXactStatusNoKeyUpdate;
4884+
old_status = MultiXactStatusNoKeyUpdate;
48774885
}
48784886

4879-
new_status = get_mxact_status_for_lock(mode, is_update);
4887+
old_mode = TUPLOCK_from_mxstatus(old_status);
48804888

48814889
/*
4882-
* If the existing lock mode is identical to or weaker than the new
4883-
* one, we can act as though there is no existing lock, so set
4884-
* XMAX_INVALID and restart.
4890+
* If the lock to be acquired is for the same TransactionId as the
4891+
* existing lock, there's an optimization possible: consider only the
4892+
* strongest of both locks as the only one present, and restart.
48854893
*/
48864894
if (xmax == add_to_xmax)
48874895
{
4888-
LockTupleMode old_mode = TUPLOCK_from_mxstatus(status);
4889-
bool old_isupd = ISUPDATE_from_mxstatus(status);
4890-
48914896
/*
4892-
* We can do this if the new LockTupleMode is higher or equal than
4893-
* the old one; and if there was previously an update, we need an
4894-
* update, but if there wasn't, then we can accept there not being
4895-
* one.
4897+
* Note that it's not possible for the original tuple to be updated:
4898+
* we wouldn't be here because the tuple would have been invisible and
4899+
* we wouldn't try to update it. As a subtlety, this code can also
4900+
* run when traversing an update chain to lock future versions of a
4901+
* tuple. But we wouldn't be here either, because the add_to_xmax
4902+
* would be different from the original updater.
48964903
*/
4897-
if ((mode >= old_mode) && (is_update || !old_isupd))
4898-
{
4899-
/*
4900-
* Note that the infomask might contain some other dirty bits.
4901-
* However, since the new infomask is reset to zero, we only
4902-
* set what's minimally necessary, and that the case that
4903-
* checks HEAP_XMAX_INVALID is the very first above, there is
4904-
* no need for extra cleanup of the infomask here.
4905-
*/
4906-
old_infomask |= HEAP_XMAX_INVALID;
4907-
goto l5;
4908-
}
4904+
Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
4905+
4906+
/* acquire the strongest of both */
4907+
if (mode < old_mode)
4908+
mode = old_mode;
4909+
/* mustn't touch is_update */
4910+
4911+
old_infomask |= HEAP_XMAX_INVALID;
4912+
goto l5;
49094913
}
4910-
new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
4914+
4915+
/* otherwise, just fall back to creating a new multixact */
4916+
new_status = get_mxact_status_for_lock(mode, is_update);
4917+
new_xmax = MultiXactIdCreate(xmax, old_status,
4918+
add_to_xmax, new_status);
49114919
GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
49124920
}
49134921
else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&

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