Skip to content

Commit d729f1e

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 4b0f7d6 commit d729f1e

File tree

1 file changed

+36
-9
lines changed

1 file changed

+36
-9
lines changed

src/backend/catalog/index.c

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2787,6 +2787,9 @@ index_update_stats(Relation rel,
27872787
bool hasindex,
27882788
double reltuples)
27892789
{
2790+
bool update_stats;
2791+
BlockNumber relpages;
2792+
BlockNumber relallvisible;
27902793
Oid relid = RelationGetRelid(rel);
27912794
Relation pg_class;
27922795
ScanKeyData key[1];
@@ -2795,6 +2798,38 @@ index_update_stats(Relation rel,
27952798
Form_pg_class rd_rel;
27962799
bool dirty;
27972800

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

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

0 commit comments

Comments
 (0)