Skip to content

Commit 6b01cac

Browse files
committed
Move I/O before the index_update_stats() buffer lock region.
Commit a07e03f enlarged the work done here under the pg_class heap buffer lock. Two preexisting actions are best done before holding that lock. Both RelationGetNumberOfBlocks() and visibilitymap_count() do I/O, and the latter might exclusive-lock a visibility map buffer. Moving these reduces contention and risk of undetected LWLock deadlock. Back-patch to v12, like that commit. Discussion: https://postgr.es/m/20241031200139.b4@rfd.leadboat.com
1 parent fe8091c commit 6b01cac

File tree

1 file changed

+36
-9
lines changed

1 file changed

+36
-9
lines changed

src/backend/catalog/index.c

+36-9
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,9 @@ index_update_stats(Relation rel,
27882788
bool hasindex,
27892789
double reltuples)
27902790
{
2791+
bool update_stats;
2792+
BlockNumber relpages;
2793+
BlockNumber relallvisible;
27912794
Oid relid = RelationGetRelid(rel);
27922795
Relation pg_class;
27932796
ScanKeyData key[1];
@@ -2796,6 +2799,38 @@ index_update_stats(Relation rel,
27962799
Form_pg_class rd_rel;
27972800
bool dirty;
27982801

2802+
/*
2803+
* As a special hack, if we are dealing with an empty table and the
2804+
* existing reltuples is -1, we leave that alone. This ensures that
2805+
* creating an index as part of CREATE TABLE doesn't cause the table to
2806+
* prematurely look like it's been vacuumed. The rd_rel we modify may
2807+
* differ from rel->rd_rel due to e.g. commit of concurrent GRANT, but the
2808+
* commands that change reltuples take locks conflicting with ours. (Even
2809+
* if a command changed reltuples under a weaker lock, this affects only
2810+
* statistics for an empty table.)
2811+
*/
2812+
if (reltuples == 0 && rel->rd_rel->reltuples < 0)
2813+
reltuples = -1;
2814+
2815+
update_stats = reltuples >= 0;
2816+
2817+
/*
2818+
* Finish I/O and visibility map buffer locks before
2819+
* systable_inplace_update_begin() locks the pg_class buffer. The rd_rel
2820+
* we modify may differ from rel->rd_rel due to e.g. commit of concurrent
2821+
* GRANT, but no command changes a relkind from non-index to index. (Even
2822+
* if one did, relallvisible doesn't break functionality.)
2823+
*/
2824+
if (update_stats)
2825+
{
2826+
relpages = RelationGetNumberOfBlocks(rel);
2827+
2828+
if (rel->rd_rel->relkind != RELKIND_INDEX)
2829+
visibilitymap_count(rel, &relallvisible, NULL);
2830+
else /* don't bother for indexes */
2831+
relallvisible = 0;
2832+
}
2833+
27992834
/*
28002835
* We always update the pg_class row using a non-transactional,
28012836
* overwrite-in-place update. There are several reasons for this:
@@ -2849,16 +2884,8 @@ index_update_stats(Relation rel,
28492884
dirty = true;
28502885
}
28512886

2852-
if (reltuples >= 0)
2887+
if (update_stats)
28532888
{
2854-
BlockNumber relpages = RelationGetNumberOfBlocks(rel);
2855-
BlockNumber relallvisible;
2856-
2857-
if (rd_rel->relkind != RELKIND_INDEX)
2858-
visibilitymap_count(rel, &relallvisible, NULL);
2859-
else /* don't bother for indexes */
2860-
relallvisible = 0;
2861-
28622889
if (rd_rel->relpages != (int32) relpages)
28632890
{
28642891
rd_rel->relpages = (int32) relpages;

0 commit comments

Comments
 (0)