diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 83d9334b791433c7c93cafc34a7c48b1c5b10d9a..a09f4d9bcd8c257e43f761eeb1fc7b7b6210298e 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -459,10 +459,13 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId) (void) GetCurrentCommandId(true); /* - * If the relation being invalidated is one of those cached in the + * If the relation being invalidated is one of those cached in the local * relcache init file, mark that we need to zap that file at commit. + * (Note: perhaps it would be better if this code were a bit more + * decoupled from the knowledge that the init file contains exactly those + * non-shared rels used in catalog caches.) */ - if (RelationIdIsInInitFile(relId)) + if (OidIsValid(dbId) && RelationSupportsSysCache(relId)) transInvalInfo->RelcacheInitFileInval = true; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 1d9844924138b001a76a974d5e697a4aaa6809ee..a8a19d48502526251ed4debd11a2fdba916cd39e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -127,14 +127,6 @@ bool criticalSharedRelcachesBuilt = false; */ static long relcacheInvalsReceived = 0L; -/* - * This list remembers the OIDs of the non-shared relations cached in the - * database's local relcache init file. Note that there is no corresponding - * list for the shared relcache init file, for reasons explained in the - * comments for RelationCacheInitFileRemove. - */ -static List *initFileRelationIds = NIL; - /* * This flag lets us optimize away work in AtEO(Sub)Xact_RelationCache(). */ @@ -3082,9 +3074,6 @@ RelationCacheInitializePhase3(void) */ InitCatalogCachePhase2(); - /* reset initFileRelationIds list; we'll fill it during write */ - initFileRelationIds = NIL; - /* now write the files */ write_relcache_init_file(true); write_relcache_init_file(false); @@ -4267,10 +4256,6 @@ load_relcache_init_file(bool shared) for (relno = 0; relno < num_rels; relno++) { RelationCacheInsert(rels[relno]); - /* also make a list of their OIDs, for RelationIdIsInInitFile */ - if (!shared) - initFileRelationIds = lcons_oid(RelationGetRelid(rels[relno]), - initFileRelationIds); } pfree(rels); @@ -4307,9 +4292,15 @@ write_relcache_init_file(bool shared) int magic; HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; - MemoryContext oldcxt; int i; + /* + * If we have already received any relcache inval events, there's no + * chance of succeeding so we may as well skip the whole thing. + */ + if (relcacheInvalsReceived != 0L) + return; + /* * We must write a temporary file and rename it into place. Otherwise, * another backend starting at about the same time might crash trying to @@ -4369,6 +4360,16 @@ write_relcache_init_file(bool shared) if (relform->relisshared != shared) continue; + /* + * Ignore if not supposed to be in init file. We can allow any shared + * relation that's been loaded so far to be in the shared init file, + * but unshared relations must be used for catalog caches. (Note: if + * you want to change the criterion for rels to be kept in the init + * file, see also inval.c.) + */ + if (!shared && !RelationSupportsSysCache(RelationGetRelid(rel))) + continue; + /* first write the relcache entry proper */ write_item(rel, sizeof(RelationData), fp); @@ -4425,15 +4426,6 @@ write_relcache_init_file(bool shared) relform->relnatts * sizeof(int16), fp); } - - /* also make a list of their OIDs, for RelationIdIsInInitFile */ - if (!shared) - { - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - initFileRelationIds = lcons_oid(RelationGetRelid(rel), - initFileRelationIds); - MemoryContextSwitchTo(oldcxt); - } } if (FreeFile(fp)) @@ -4492,21 +4484,6 @@ write_item(const void *data, Size len, FILE *fp) elog(FATAL, "could not write init file"); } -/* - * Detect whether a given relation (identified by OID) is one of the ones - * we store in the local relcache init file. - * - * Note that we effectively assume that all backends running in a database - * would choose to store the same set of relations in the init file; - * otherwise there are cases where we'd fail to detect the need for an init - * file invalidation. This does not seem likely to be a problem in practice. - */ -bool -RelationIdIsInInitFile(Oid relationId) -{ - return list_member_oid(initFileRelationIds, relationId); -} - /* * Invalidate (remove) the init file during commit of a transaction that * changed one or more of the relation cache entries that are kept in the diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 6b33383411165f297072c045661022691c4eef8c..cf8d0fd2513e6954e2c2fb82a9bae1298af830cb 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -766,11 +766,18 @@ static const struct cachedesc cacheinfo[] = { } }; -static CatCache *SysCache[ - lengthof(cacheinfo)]; -static int SysCacheSize = lengthof(cacheinfo); +#define SysCacheSize ((int) lengthof(cacheinfo)) + +static CatCache *SysCache[SysCacheSize]; + static bool CacheInitialized = false; +/* Sorted array of OIDs of tables and indexes used by caches */ +static Oid SysCacheSupportingRelOid[SysCacheSize * 2]; +static int SysCacheSupportingRelOidSize; + +static int oid_compare(const void *a, const void *b); + /* * InitCatalogCache - initialize the caches @@ -784,10 +791,12 @@ void InitCatalogCache(void) { int cacheId; + int i, + j; Assert(!CacheInitialized); - MemSet(SysCache, 0, sizeof(SysCache)); + SysCacheSupportingRelOidSize = 0; for (cacheId = 0; cacheId < SysCacheSize; cacheId++) { @@ -800,7 +809,25 @@ InitCatalogCache(void) if (!PointerIsValid(SysCache[cacheId])) elog(ERROR, "could not initialize cache %u (%d)", cacheinfo[cacheId].reloid, cacheId); + /* Accumulate data for OID lists, too */ + SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] = + cacheinfo[cacheId].reloid; + SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] = + cacheinfo[cacheId].indoid; + } + + Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid)); + + /* Sort and de-dup OID arrays, so we can use binary search. */ + pg_qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize, + sizeof(Oid), oid_compare); + for (i = 1, j = 0; i < SysCacheSupportingRelOidSize; i++) + { + if (SysCacheSupportingRelOid[i] != SysCacheSupportingRelOid[j]) + SysCacheSupportingRelOid[++j] = SysCacheSupportingRelOid[i]; } + SysCacheSupportingRelOidSize = j + 1; + CacheInitialized = true; } @@ -1088,3 +1115,43 @@ SearchSysCacheList(int cacheId, int nkeys, return SearchCatCacheList(SysCache[cacheId], nkeys, key1, key2, key3, key4); } + +/* + * Test whether a relation supports a system cache, ie it is either a + * cached table or the index used for a cache. + */ +bool +RelationSupportsSysCache(Oid relid) +{ + int low = 0, + high = SysCacheSupportingRelOidSize - 1; + + while (low <= high) + { + int middle = low + (high - low) / 2; + + if (SysCacheSupportingRelOid[middle] == relid) + return true; + if (SysCacheSupportingRelOid[middle] < relid) + low = middle + 1; + else + high = middle - 1; + } + + return false; +} + + +/* + * OID comparator for pg_qsort + */ +static int +oid_compare(const void *a, const void *b) +{ + Oid oa = *((const Oid *) a); + Oid ob = *((const Oid *) b); + + if (oa == ob) + return 0; + return (oa > ob) ? 1 : -1; +} diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 88dbcb9dd61419abbe805c1849ea7bc15bdc41ee..15304209679cdeb3d4b8ca4ac57b06cc4bde61ab 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -96,7 +96,6 @@ extern void AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid, /* * Routines to help manage rebuilding of relcache init files */ -extern bool RelationIdIsInInitFile(Oid relationId); extern void RelationCacheInitFilePreInvalidate(void); extern void RelationCacheInitFilePostInvalidate(void); extern void RelationCacheInitFileRemove(void); diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index d59dd4e0c7038fa2796f4d30855548b31a7daad7..6998ab784ada95eab8278cc888aa8738a703cbb8 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -120,6 +120,8 @@ extern uint32 GetSysCacheHashValue(int cacheId, extern struct catclist *SearchSysCacheList(int cacheId, int nkeys, Datum key1, Datum key2, Datum key3, Datum key4); +extern bool RelationSupportsSysCache(Oid relid); + /* * The use of the macros below rather than direct calls to the corresponding * functions is encouraged, as it insulates the caller from changes in the