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

Commit 85d3b3c

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 db1014b commit 85d3b3c

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
@@ -4534,13 +4534,20 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
45344534
uint16 new_infomask,
45354535
new_infomask2;
45364536

4537+
Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
4538+
45374539
l5:
45384540
new_infomask = 0;
45394541
new_infomask2 = 0;
45404542
if (old_infomask & HEAP_XMAX_INVALID)
45414543
{
45424544
/*
45434545
* No previous locker; we just insert our own TransactionId.
4546+
*
4547+
* Note that it's critical that this case be the first one checked,
4548+
* because there are several blocks below that come back to this one
4549+
* to implement certain optimizations; old_infomask might contain
4550+
* other dirty bits in those cases, but we don't really care.
45444551
*/
45454552
if (is_update)
45464553
{
@@ -4666,21 +4673,22 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
46664673
* create a new MultiXactId that includes both the old locker or
46674674
* updater and our own TransactionId.
46684675
*/
4669-
MultiXactStatus status;
46704676
MultiXactStatus new_status;
4677+
MultiXactStatus old_status;
4678+
LockTupleMode old_mode;
46714679

46724680
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
46734681
{
46744682
if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
4675-
status = MultiXactStatusForKeyShare;
4683+
old_status = MultiXactStatusForKeyShare;
46764684
else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
4677-
status = MultiXactStatusForShare;
4685+
old_status = MultiXactStatusForShare;
46784686
else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
46794687
{
46804688
if (old_infomask2 & HEAP_KEYS_UPDATED)
4681-
status = MultiXactStatusForUpdate;
4689+
old_status = MultiXactStatusForUpdate;
46824690
else
4683-
status = MultiXactStatusForNoKeyUpdate;
4691+
old_status = MultiXactStatusForNoKeyUpdate;
46844692
}
46854693
else
46864694
{
@@ -4700,43 +4708,43 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
47004708
{
47014709
/* it's an update, but which kind? */
47024710
if (old_infomask2 & HEAP_KEYS_UPDATED)
4703-
status = MultiXactStatusUpdate;
4711+
old_status = MultiXactStatusUpdate;
47044712
else
4705-
status = MultiXactStatusNoKeyUpdate;
4713+
old_status = MultiXactStatusNoKeyUpdate;
47064714
}
47074715

4708-
new_status = get_mxact_status_for_lock(mode, is_update);
4716+
old_mode = TUPLOCK_from_mxstatus(old_status);
47094717

47104718
/*
4711-
* If the existing lock mode is identical to or weaker than the new
4712-
* one, we can act as though there is no existing lock, so set
4713-
* XMAX_INVALID and restart.
4719+
* If the lock to be acquired is for the same TransactionId as the
4720+
* existing lock, there's an optimization possible: consider only the
4721+
* strongest of both locks as the only one present, and restart.
47144722
*/
47154723
if (xmax == add_to_xmax)
47164724
{
4717-
LockTupleMode old_mode = TUPLOCK_from_mxstatus(status);
4718-
bool old_isupd = ISUPDATE_from_mxstatus(status);
4719-
47204725
/*
4721-
* We can do this if the new LockTupleMode is higher or equal than
4722-
* the old one; and if there was previously an update, we need an
4723-
* update, but if there wasn't, then we can accept there not being
4724-
* one.
4726+
* Note that it's not possible for the original tuple to be updated:
4727+
* we wouldn't be here because the tuple would have been invisible and
4728+
* we wouldn't try to update it. As a subtlety, this code can also
4729+
* run when traversing an update chain to lock future versions of a
4730+
* tuple. But we wouldn't be here either, because the add_to_xmax
4731+
* would be different from the original updater.
47254732
*/
4726-
if ((mode >= old_mode) && (is_update || !old_isupd))
4727-
{
4728-
/*
4729-
* Note that the infomask might contain some other dirty bits.
4730-
* However, since the new infomask is reset to zero, we only
4731-
* set what's minimally necessary, and that the case that
4732-
* checks HEAP_XMAX_INVALID is the very first above, there is
4733-
* no need for extra cleanup of the infomask here.
4734-
*/
4735-
old_infomask |= HEAP_XMAX_INVALID;
4736-
goto l5;
4737-
}
4733+
Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
4734+
4735+
/* acquire the strongest of both */
4736+
if (mode < old_mode)
4737+
mode = old_mode;
4738+
/* mustn't touch is_update */
4739+
4740+
old_infomask |= HEAP_XMAX_INVALID;
4741+
goto l5;
47384742
}
4739-
new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
4743+
4744+
/* otherwise, just fall back to creating a new multixact */
4745+
new_status = get_mxact_status_for_lock(mode, is_update);
4746+
new_xmax = MultiXactIdCreate(xmax, old_status,
4747+
add_to_xmax, new_status);
47404748
GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
47414749
}
47424750
else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&

0 commit comments

Comments
 (0)