diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 62c4369b171..7e4bef3c6ba 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -461,10 +461,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 296c0631ee1..f5e43d486f4 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -130,14 +130,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; - /* * eoxact_list[] stores the OIDs of relations that (might) need AtEOXact * cleanup work. This list intentionally has limited size; if it overflows, @@ -3190,9 +3182,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); @@ -4477,10 +4466,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); @@ -4517,9 +4502,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 @@ -4579,6 +4570,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); @@ -4635,15 +4636,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)) @@ -4702,21 +4694,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 9aa5ee5ac05..94fc568d6e7 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -791,11 +791,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 @@ -809,10 +816,12 @@ void InitCatalogCache(void) { int cacheId; + int i, + j; Assert(!CacheInitialized); - MemSet(SysCache, 0, sizeof(SysCache)); + SysCacheSupportingRelOidSize = 0; for (cacheId = 0; cacheId < SysCacheSize; cacheId++) { @@ -825,7 +834,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; } @@ -1113,3 +1140,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 39b98f2568e..49c84b8cea8 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -105,7 +105,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 d1d8abe0a90..8dc00102bfe 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -18,7 +18,7 @@ #include "access/attnum.h" #include "access/htup.h" -/* we purposedly do not include utils/catcache.h here */ +/* we intentionally do not include utils/catcache.h here */ /* * SysCache identifiers. @@ -125,6 +125,8 @@ struct catclist; 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