8000 Avoid returning stale attribute bitmaps in RelationGetIndexAttrBitmap(). · intobs/postgres@5879958 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5879958

Browse files
committed
Avoid returning stale attribute bitmaps in RelationGetIndexAttrBitmap().
The problem with the original coding here is that we might receive (and clear) a relcache invalidation signal for the target relation down inside one of the index_open calls we're doing. Since the target is open, we would not drop the relcache entry, just reset its rd_indexvalid and rd_indexlist fields. But RelationGetIndexAttrBitmap() kept going, and would eventually cache and return potentially-obsolete attribute bitmaps. The case where this matters is where the inval signal was from a CREATE INDEX CONCURRENTLY telling us about a new index on a formerly-unindexed column. (In all other cases, the lock we hold on the target rel should prevent any concurrent change in index state.) Even just returning the stale attribute bitmap is not such a problem, because it shouldn't matter during the transaction in which we receive the signal. What hurts is caching the stale data, because it can survive into later transactions, breaking CREATE INDEX CONCURRENTLY's expectation that later transactions will not create new broken HOT chains. The upshot is that there's a window for building corrupted indexes during CREATE INDEX CONCURRENTLY. This patch fixes the problem by rechecking that the set of index OIDs is still the same at the end of RelationGetIndexAttrBitmap() as it was at the start. If not, we loop back and try again. That's a little more than is strictly necessary to fix the bug --- in principle, we could return the stale data but not cache it --- but it seems like a bad idea on general principles for relcache to return data it knows is stale. There might be more hazards of the same ilk, or there might be a better way to fix this one, but this patch definitely improves matters and seems unlikely to make anything worse. So let's push it into today's releases even as we continue to study the problem. Pavan Deolasee and myself Discussion: https://postgr.es/m/CABOikdM2MUq9cyZJi1KyLmmkCereyGp5JQ4fuwKoyKEde_mzkQ@mail.gmail.com
1 parent 468d108 commit 5879958

File tree

1 file changed

+33
-6
lines changed

1 file changed

+33
-6
lines changed

src/backend/utils/cache/relcache.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4054,8 +4054,10 @@ RelationGetIndexPredicate(Relation relation)
40544054
* we can include system attributes (e.g., OID) in the bitmap representation.
40554055
*
40564056
* Caller had better hold at least RowExclusiveLock on the target relation
4057-
* to ensure that it has a stable set of indexes. This also makes it safe
4058-
* (deadlock-free) for us to take locks on the relation's indexes.
4057+
* to ensure it is safe (deadlock-free) for us to take locks on the relation's
4058+
* indexes. Note that since the introduction of CREATE INDEX CONCURRENTLY,
4059+
* that lock level doesn't guarantee a stable set of indexes, so we have to
4060+
* be prepared to retry here in case of a change in the set of indexes.
40594061
*
40604062
* The returned result is palloc'd in the caller's memory context and should
40614063
* be bms_free'd when not needed anymore.
@@ -4067,6 +4069,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40674069
Bitmapset *uindexattrs; /* columns in unique indexes */
40684070
Bitmapset *idindexattrs; /* columns in the replica identity */
40694071
List *indexoidlist;
4072+
List *newindexoidlist;
40704073
Oid relreplindex;
40714074
ListCell *l;
40724075
MemoryContext oldcxt;
@@ -4092,8 +4095,9 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40924095
return NULL;
40934096

40944097
/*
4095-
* Get cached list of index OIDs
4098+
* Get cached list of index OIDs. If we have to start over, we do so here.
40964099
*/
4100+
restart:
40974101
indexoidlist = RelationGetIndexList(relation);
40984102

40994103
/* Fall out if no indexes (but relhasindex was set) */
@@ -4104,8 +4108,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
41044108
* Copy the rd_replidindex value computed by RelationGetIndexList before
41054109
* proceeding. This is needed because a relcache flush could occur inside
41064110
* index_open below, resetting the fields managed by 8000 RelationGetIndexList.
4107-
* (The values we're computing will still be valid, assuming that caller
4108-
* has a sufficient lock on the relation.)
4111+
* We need to do the work with a stable value for relreplindex.
41094112
*/
41104113
relreplindex = relation->rd_replidindex;
41114114

@@ -4173,7 +4176,31 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
41734176
index_close(indexDesc, AccessShareLock);
41744177
}
41754178

4176-
list_free(indexoidlist);
4179+
/*
4180+
* During one of the index_opens in the above loop, we might have received
4181+
* a relcache flush event on this relcache entry, which might have been
4182+
* signaling a change in the rel's index list. If so, we'd better start
4183+
* over to ensure we deliver up-to-date attribute bitmaps.
4184+
*/
4185+
newindexoidlist = RelationGetIndexList(relation);
4186+
if (equal(indexoidlist, newindexoidlist) &&
4187+
relreplindex == relation->rd_replidindex)
4188+
{
4189 8000 +
/* Still the same index set, so proceed */
4190+
list_free(newindexoidlist);
4191+
list_free(indexoidlist);
4192+
}
4193+
else
4194+
{
4195+
/* Gotta do it over ... might as well not leak memory */
4196+
list_free(newindexoidlist);
4197+
list_free(indexoidlist);
4198+
bms_free(uindexattrs);
4199+
bms_free(idindexattrs);
4200+
bms_free(indexattrs);
4201+
4202+
goto restart;
4203+
}
41774204

41784205
/*
41794206
* Now save copies of the bitmaps in the relcache entry. We intentionally

0 commit comments

Comments
 (0)
0