Skip to content

Commit 2fd8685

Browse files
committed
Simplify check of modified attributes in heap_update
The old coding was getting more complicated as new things were added, and it would be barely tolerable with upcoming WARM updates and other future features such as indirect indexes. The new coding incurs a small performance cost in synthetic benchmark cases, and is barely measurable in normal cases. A much larger benefit is expected from WARM, which could actually bolt its needs on top of the existing coding, but it is much uglier and bug-prone than doing it on this new code. Additional optimization can be applied on top of this, if need be. Reviewed-by: Pavan Deolasee, Amit Kapila, Mithun CY Discussion: https://postgr.es/m/20161228232018.4hc66ndrzpz4g4wn@alvherre.pgsql https://postgr.es/m/CABOikdMJfz69dBNRTOZcB6s5A0tf8OMCyQVYQyR-WFFdoEwKMQ@mail.gmail.com
1 parent 9a09527 commit 2fd8685

File tree

1 file changed

+71
-126
lines changed

1 file changed

+71
-126
lines changed

src/backend/access/heap/heapam.c

+71-126
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,8 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
9696
Buffer newbuf, HeapTuple oldtup,
9797
HeapTuple newtup, HeapTuple old_key_tup,
9898
bool all_visible_cleared, bool new_all_visible_cleared);
99-
static void HeapSatisfiesHOTandKeyUpdate(Relation relation,
100-
Bitmapset *hot_attrs,
101-
Bitmapset *key_attrs, Bitmapset *id_attrs,
102-
bool *satisfies_hot, bool *satisfies_key,
103-
bool *satisfies_id,
99+
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
100+
Bitmapset *interesting_cols,
104101
HeapTuple oldtup, HeapTuple newtup);
105102
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
106103
LockTupleMode mode, LockWaitPolicy wait_policy,
@@ -3471,6 +3468,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
34713468
Bitmapset *hot_attrs;
34723469
Bitmapset *key_attrs;
34733470
Bitmapset *id_attrs;
3471+
Bitmapset *interesting_attrs;
3472+
Bitmapset *modified_attrs;
34743473
ItemId lp;
34753474
HeapTupleData oldtup;
34763475
HeapTuple heaptup;
@@ -3488,10 +3487,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
34883487
pagefree;
34893488
bool have_tuple_lock = false;
34903489
bool iscombo;
3491-
bool satisfies_hot;
3492-
bool satisfies_key;
3493-
bool satisfies_id;
34943490
bool use_hot_update = false;
3491+
bool hot_attrs_checked = false;
34953492
bool key_intact;
34963493
bool all_visible_cleared = false;
34973494
bool all_visible_cleared_new = false;
@@ -3517,26 +3514,50 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35173514
errmsg("cannot update tuples during a parallel operation")));
35183515

35193516
/*
3520-
* Fetch the list of attributes to be checked for HOT update. This is
3521-
* wasted effort if we fail to update or have to put the new tuple on a
3522-
* different page. But we must compute the list before obtaining buffer
3523-
* lock --- in the worst case, if we are doing an update on one of the
3524-
* relevant system catalogs, we could deadlock if we try to fetch the list
3525-
* later. In any case, the relcache caches the data so this is usually
3526-
* pretty cheap.
3517+
* Fetch the list of attributes to be checked for various operations.
35273518
*
3528-
* Note that we get a copy here, so we need not worry about relcache flush
3529-
* happening midway through.
3519+
* For HOT considerations, this is wasted effort if we fail to update or
3520+
* have to put the new tuple on a different page. But we must compute the
3521+
* list before obtaining buffer lock --- in the worst case, if we are doing
3522+
* an update on one of the relevant system catalogs, we could deadlock if
3523+
* we try to fetch the list later. In any case, the relcache caches the
3524+
* data so this is usually pretty cheap.
3525+
*
3526+
* We also need columns used by the replica identity and columns that are
3527+
* considered the "key" of rows in the table.
3528+
*
3529+
* Note that we get copies of each bitmap, so we need not worry about
3530+
* relcache flush happening midway through.
35303531
*/
35313532
hot_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_ALL);
35323533
key_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_KEY);
35333534
id_attrs = RelationGetIndexAttrBitmap(relation,
35343535
INDEX_ATTR_BITMAP_IDENTITY_KEY);
35353536

3537+
35363538
block = ItemPointerGetBlockNumber(otid);
35373539
buffer = ReadBuffer(relation, block);
35383540
page = BufferGetPage(buffer);
35393541

3542+
interesting_attrs = NULL;
3543+
/*
3544+
* If the page is already full, there is hardly any chance of doing a HOT
3545+
* update on this page. It might be wasteful effort to look for index
3546+
* column updates only to later reject HOT updates for lack of space in the
3547+
* same page. So we be conservative and only fetch hot_attrs if the page is
3548+
* not already full. Since we are already holding a pin on the buffer,
3549+
* there is no chance that the buffer can get cleaned up concurrently and
3550+
* even if that was possible, in the worst case we lose a chance to do a
3551+
* HOT update.
3552+
*/
3553+
if (!PageIsFull(page))
3554+
{
3555+
interesting_attrs = bms_add_members(interesting_attrs, hot_attrs);
3556+
hot_attrs_checked = true;
3557+
}
3558+
interesting_attrs = bms_add_members(interesting_attrs, key_attrs);
3559+
interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
3560+
35403561
/*
35413562
* Before locking the buffer, pin the visibility map page if it appears to
35423563
* be necessary. Since we haven't got the lock yet, someone else might be
@@ -3552,7 +3573,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35523573
Assert(ItemIdIsNormal(lp));
35533574

35543575
/*
3555-
* Fill in enough data in oldtup for HeapSatisfiesHOTandKeyUpdate to work
3576+
* Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
35563577
* properly.
35573578
*/
35583579
oldtup.t_tableOid = RelationGetRelid(relation);
@@ -3578,6 +3599,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35783599
Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
35793600
}
35803601

3602+
/* Determine columns modified by the update. */
3603+
modified_attrs = HeapDetermineModifiedColumns(relation, interesting_attrs,
3604+
&oldtup, newtup);
3605+
35813606
/*
35823607
* If we're not updating any "key" column, we can grab a weaker lock type.
35833608
* This allows for more concurrency when we are running simultaneously
@@ -3589,10 +3614,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35893614
* is updates that don't manipulate key columns, not those that
35903615
* serendipitiously arrive at the same key values.
35913616
*/
3592-
HeapSatisfiesHOTandKeyUpdate(relation, hot_attrs, key_attrs, id_attrs,
3593-
&satisfies_hot, &satisfies_key,
3594-
&satisfies_id, &oldtup, newtup);
3595-
if (satisfies_key)
3617+
if (!bms_overlap(modified_attrs, key_attrs))
35963618
{
35973619
*lockmode = LockTupleNoKeyExclusive;
35983620
mxact_status = MultiXactStatusNoKeyUpdate;
@@ -3831,6 +3853,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38313853
bms_free(hot_attrs);
38323854
bms_free(key_attrs);
38333855
bms_free(id_attrs);
3856+
bms_free(modified_attrs);
3857+
bms_free(interesting_attrs);
38343858
return result;
38353859
}
38363860

@@ -4133,9 +4157,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
41334157
/*
41344158
* Since the new tuple is going into the same page, we might be able
41354159
* to do a HOT update. Check if any of the index columns have been
4136-
* changed. If not, then HOT update is possible.
4160+
* changed. If the page was already full, we may have skipped checking
4161+
* for index columns. If so, HOT update is possible.
41374162
*/
4138-
if (satisfies_hot)
4163+
if (hot_attrs_checked && !bms_overlap(modified_attrs, hot_attrs))
41394164
use_hot_update = true;
41404165
}
41414166
else
@@ -4150,7 +4175,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
41504175
* ExtractReplicaIdentity() will return NULL if nothing needs to be
41514176
* logged.
41524177
*/
4153-
old_key_tuple = ExtractReplicaIdentity(relation, &oldtup, !satisfies_id, &old_key_copied);
4178+
old_key_tuple = ExtractReplicaIdentity(relation, &oldtup,
4179+
bms_overlap(modified_attrs, id_attrs),
4180+
&old_key_copied);
41544181

41554182
/* NO EREPORT(ERROR) from here till changes are logged */
41564183
START_CRIT_SECTION();
@@ -4298,13 +4325,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
42984325
bms_free(hot_attrs);
42994326
bms_free(key_attrs);
43004327
bms_free(id_attrs);
4328+
bms_free(modified_attrs);
4329+
bms_free(interesting_attrs);
43014330

43024331
return HeapTupleMayBeUpdated;
43034332
}
43044333

43054334
/*
43064335
* Check if the specified attribute's value is same in both given tuples.
4307-
* Subroutine for HeapSatisfiesHOTandKeyUpdate.
4336+
* Subroutine for HeapDetermineModifiedColumns.
43084337
*/
43094338
static bool
43104339
heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
@@ -4338,7 +4367,7 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
43384367

43394368
/*
43404369
* Extract the corresponding values. XXX this is pretty inefficient if
4341-
* there are many indexed columns. Should HeapSatisfiesHOTandKeyUpdate do
4370+
* there are many indexed columns. Should HeapDetermineModifiedColumns do
43424371
* a single heap_deform_tuple call on each tuple, instead? But that
43434372
* doesn't work for system columns ...
43444373
*/
@@ -4383,114 +4412,30 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
43834412
/*
43844413
* Check which columns are being updated.
43854414
*
4386-
* This simultaneously checks conditions for HOT updates, for FOR KEY
4387-
* SHARE updates, and REPLICA IDENTITY concerns. Since much of the time they
4388-
* will be checking very similar sets of columns, and doing the same tests on
4389-
* them, it makes sense to optimize and do them together.
4390-
*
4391-
* We receive three bitmapsets comprising the three sets of columns we're
4392-
* interested in. Note these are destructively modified; that is OK since
4393-
* this is invoked at most once in heap_update.
4415+
* Given an updated tuple, determine (and return into the output bitmapset),
4416+
* from those listed as interesting, the set of columns that changed.
43944417
*
4395-
* hot_result is set to TRUE if it's okay to do a HOT update (i.e. it does not
4396-
* modified indexed columns); key_result is set to TRUE if the update does not
4397-
* modify columns used in the key; id_result is set to TRUE if the update does
4398-
* not modify columns in any index marked as the REPLICA IDENTITY.
4418+
* The input bitmapset is destructively modified; that is OK since this is
4419+
* invoked at most once in heap_update.
43994420
*/
4400-
static void
4401-
HeapSatisfiesHOTandKeyUpdate(Relation relation, Bitmapset *hot_attrs,
4402-
Bitmapset *key_attrs, Bitmapset *id_attrs,
4403-
bool *satisfies_hot, bool *satisfies_key,
4404-
bool *satisfies_id,
4421+
static Bitmapset *
4422+
HeapDetermineModifiedColumns(Relation relation, Bitmapset *interesting_cols,
44054423
HeapTuple oldtup, HeapTuple newtup)
44064424
{
4407-
int next_hot_attnum;
4408-
int next_key_attnum;
4409-
int next_id_attnum;
4410-
bool hot_result = true;
4411-
bool key_result = true;
4412-
bool id_result = true;
4413-
4414-
/* If REPLICA IDENTITY is set to FULL, id_attrs will be empty. */
4415-
Assert(bms_is_subset(id_attrs, key_attrs));
4416-
Assert(bms_is_subset(key_attrs, hot_attrs));
4417-
4418-
/*
4419-
* If one of these sets contains no remaining bits, bms_first_member will
4420-
* return -1, and after adding FirstLowInvalidHeapAttributeNumber (which
4421-
* is negative!) we'll get an attribute number that can't possibly be
4422-
* real, and thus won't match any actual attribute number.
4423-
*/
4424-
next_hot_attnum = bms_first_member(hot_attrs);
4425-
next_hot_attnum += FirstLowInvalidHeapAttributeNumber;
4426-
next_key_attnum = bms_first_member(key_attrs);
4427-
next_key_attnum += FirstLowInvalidHeapAttributeNumber;
4428-
next_id_attnum = bms_first_member(id_attrs);
4429-
next_id_attnum += FirstLowInvalidHeapAttributeNumber;
4425+
int attnum;
4426+
Bitmapset *modified = NULL;
44304427

4431-
for (;;)
4428+
while ((attnum = bms_first_member(interesting_cols)) >= 0)
44324429
{
4433-
bool changed;
4434-
int check_now;
4430+
attnum += FirstLowInvalidHeapAttributeNumber;
44354431

4436-
/*
4437-
* Since the HOT attributes are a superset of the key attributes and
4438-
* the key attributes are a superset of the id attributes, this logic
4439-
* is guaranteed to identify the next column that needs to be checked.
4440-
*/
4441-
if (hot_result && next_hot_attnum > FirstLowInvalidHeapAttributeNumber)
4442-
check_now = next_hot_attnum;
4443-
else if (key_result && next_key_attnum > FirstLowInvalidHeapAttributeNumber)
4444-
check_now = next_key_attnum;
4445-
else if (id_result && next_id_attnum > FirstLowInvalidHeapAttributeNumber)
4446-
check_now = next_id_attnum;
4447-
else
4448-
break;
4449-
4450-
/* See whether it changed. */
4451-
changed = !heap_tuple_attr_equals(RelationGetDescr(relation),
4452-
check_now, oldtup, newtup);
4453-
if (changed)
4454-
{
4455-
if (check_now == next_hot_attnum)
4456-
hot_result = false;
4457-
if (check_now == next_key_attnum)
4458-
key_result = false;
4459-
if (check_now == next_id_attnum)
4460-
id_result = false;
4461-
4462-
/* if all are false now, we can stop checking */
4463-
if (!hot_result && !key_result && !id_result)
4464-
break;
4465-
}
4466-
4467-
/*
4468-
* Advance the next attribute numbers for the sets that contain the
4469-
* attribute we just checked. As we work our way through the columns,
4470-
* the next_attnum values will rise; but when each set becomes empty,
4471-
* bms_first_member() will return -1 and the attribute number will end
4472-
* up with a value less than FirstLowInvalidHeapAttributeNumber.
4473-
*/
4474-
if (hot_result && check_now == next_hot_attnum)
4475-
{
4476-
next_hot_attnum = bms_first_member(hot_attrs);
4477-
next_hot_attnum += FirstLowInvalidHeapAttributeNumber;
4478-
}
4479-
if (key_result && check_now == next_key_attnum)
4480-
{
4481-
next_key_attnum = bms_first_member(key_attrs);
4482-
next_key_attnum += FirstLowInvalidHeapAttributeNumber;
4483-
}
4484-
if (id_result && check_now == next_id_attnum)
4485-
{
4486-
next_id_attnum = bms_first_member(id_attrs);
4487-
next_id_attnum += FirstLowInvalidHeapAttributeNumber;
4488-
}
4432+
if (!heap_tuple_attr_equals(RelationGetDescr(relation),
4433+
attnum, oldtup, newtup))
4434+
modified = bms_add_member(modified,
4435+
attnum - FirstLowInvalidHeapAttributeNumber);
44894436
}
44904437

4491-
*satisfies_hot = hot_result;
4492-
*satisfies_key = key_result;
4493-
*satisfies_id = id_result;
4438+
return modified;
44944439
}
44954440

44964441
/*

0 commit comments

Comments
 (0)