Skip to content

Commit 0bcb9d0

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 c1099dd commit 0bcb9d0

File tree

1 file changed

+40
-22
lines changed

1 file changed

+40
-22
lines changed

src/backend/catalog/index.c

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2782,6 +2782,9 @@ index_update_stats(Relation rel,
27822782
bool hasindex,
27832783
double reltuples)
27842784
{
2785+
bool update_stats;
2786+
BlockNumber relpages;
2787+
BlockNumber relallvisible;
27852788
Oid relid = RelationGetRelid(rel);
27862789
Relation pg_class;
27872790
ScanKeyData key[1];
@@ -2790,6 +2793,42 @@ index_update_stats(Relation rel,
27902793
Form_pg_class rd_rel;
27912794
bool dirty;
27922795

2796+
/*
2797+
* As a special hack, if we are dealing with an empty table and the
2798+
* existing reltuples is -1, we leave that alone. This ensures that
2799+
* creating an index as part of CREATE TABLE doesn't cause the table to
2800+
* prematurely look like it's been vacuumed. The rd_rel we modify may
2801+
* differ from rel->rd_rel due to e.g. commit of concurrent GRANT, but the
2802+
* commands that change reltuples take locks conflicting with ours. (Even
2803+
* if a command changed reltuples under a weaker lock, this affects only
2804+
* statistics for an empty table.)
2805+
*/
2806+
if (reltuples == 0 && rel->rd_rel->reltuples < 0)
2807+
reltuples = -1;
2808+
2809+
/*
2810+
* Don't update statistics during binary upgrade, because the indexes are
2811+
* created before the data is moved into place.
2812+
*/
2813+
update_stats = reltuples >= 0 && !IsBinaryUpgrade;
2814+
2815+
/*
2816+
* Finish I/O and visibility map buffer locks before
2817+
* systable_inplace_update_begin() locks the pg_class buffer. The rd_rel
2818+
* we modify may differ from rel->rd_rel due to e.g. commit of concurrent
2819+
* GRANT, but no command changes a relkind from non-index to index. (Even
2820+
* if one did, relallvisible doesn't break functionality.)
2821+
*/
2822+
if (update_stats)
2823+
{
2824+
relpages = RelationGetNumberOfBlocks(rel);
2825+
2826+
if (rel->rd_rel->relkind != RELKIND_INDEX)
2827+
visibilitymap_count(rel, &relallvisible, NULL);
2828+
else /* don't bother for indexes */
2829+
relallvisible = 0;
2830+
}
2831+
27932832
/*
27942833
* We always update the pg_class row using a non-transactional,
27952834
* overwrite-in-place update. There are several reasons for this:
@@ -2834,15 +2873,6 @@ index_update_stats(Relation rel,
28342873
/* Should this be a more comprehensive test? */
28352874
Assert(rd_rel->relkind != RELKIND_PARTITIONED_INDEX);
28362875

2837-
/*
2838-
* As a special hack, if we are dealing with an empty table and the
2839-
* existing reltuples is -1, we leave that alone. This ensures that
2840-
* creating an index as part of CREATE TABLE doesn't cause the table to
2841-
* prematurely look like it's been vacuumed.
2842-
*/
2843-
if (reltuples == 0 && rd_rel->reltuples < 0)
2844-
reltuples = -1;
2845-
28462876
/* Apply required updates, if any, to copied tuple */
28472877

28482878
dirty = false;
@@ -2852,20 +2882,8 @@ index_update_stats(Relation rel,
28522882
dirty = true;
28532883
}
28542884

2855-
/*
2856-
* Avoid updating statistics during binary upgrade, because the indexes
2857-
* are created before the data is moved into place.
2858-
*/
2859-
if (reltuples >= 0 && !IsBinaryUpgrade)
2885+
if (update_stats)
28602886
{
2861-
BlockNumber relpages = RelationGetNumberOfBlocks(rel);
2862-
BlockNumber relallvisible;
2863-
2864-
if (rd_rel->relkind != RELKIND_INDEX)
2865-
visibilitymap_count(rel, &relallvisible, NULL);
2866-
else /* don't bother for indexes */
2867-
relallvisible = 0;
2868-
28692887
if (rd_rel->relpages != (int32) relpages)
28702888
{
28712889
rd_rel->relpages = (int32) relpages;

0 commit comments

Comments
 (0)