aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2017-05-12 18:17:29 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2017-05-12 18:17:29 -0400
commit50ee1c7462d796639eef6c24b1797df8c4d6c098 (patch)
tree27ca7b421bcbd1999e1760e6d62cb2bc82c4d792 /src/backend/utils/cache
parent928c4de30991ca24a46a92f006892c039af30833 (diff)
downloadpostgresql-50ee1c7462d796639eef6c24b1797df8c4d6c098.tar.gz
postgresql-50ee1c7462d796639eef6c24b1797df8c4d6c098.zip
Avoid searching for the target catcache in CatalogCacheIdInvalidate.
A test case provided by Mathieu Fenniak shows that the initial search for the target catcache in CatalogCacheIdInvalidate consumes a very significant amount of overhead in cases where cache invalidation is triggered but has little useful work to do. There is no good reason for that search to exist at all, as the index array maintained by syscache.c allows direct lookup of the catcache from its ID. We just need a frontend function in syscache.c, matching the division of labor for most other cache-accessing operations. While there's more that can be done in this area, this patch alone reduces the runtime of Mathieu's example by 2X. We can hope that it offers some useful benefit in other cases too, although usually cache invalidation overhead is not such a striking fraction of the total runtime. Back-patch to 9.4 where logical decoding was introduced. It might be worth going further back, but presently the only case we know of where cache invalidation is really a significant burden is in logical decoding. Also, older branches have fewer catcaches, reducing the possible benefit. (Note: although this nominally changes catcache's API, we have always documented CatalogCacheIdInvalidate as a private function, so I would have little sympathy for an external module calling it directly. So backpatching should be fine.) Discussion: https://postgr.es/m/CAHoiPjzea6N0zuCi=+f9v_j94nfsy6y8SU7-=bp4=7qw6_i=Rg@mail.gmail.com
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r--src/backend/utils/cache/catcache.c92
-rw-r--r--src/backend/utils/cache/inval.c2
-rw-r--r--src/backend/utils/cache/syscache.c21
3 files changed, 61 insertions, 54 deletions
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index c27186fa13b..b19044c46ca 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -422,7 +422,7 @@ CatCacheRemoveCList(CatCache *cache, CatCList *cl)
/*
- * CatalogCacheIdInvalidate
+ * CatCacheInvalidate
*
* Invalidate entries in the specified cache, given a hash value.
*
@@ -440,71 +440,57 @@ CatCacheRemoveCList(CatCache *cache, CatCList *cl)
* This routine is only quasi-public: it should only be used by inval.c.
*/
void
-CatalogCacheIdInvalidate(int cacheId, uint32 hashValue)
+CatCacheInvalidate(CatCache *cache, uint32 hashValue)
{
- slist_iter cache_iter;
+ Index hashIndex;
+ dlist_mutable_iter iter;
- CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called");
+ CACHE1_elog(DEBUG2, "CatCacheInvalidate: called");
/*
- * inspect caches to find the proper cache
+ * We don't bother to check whether the cache has finished initialization
+ * yet; if not, there will be no entries in it so no problem.
*/
- slist_foreach(cache_iter, &CacheHdr->ch_caches)
- {
- CatCache *ccp = slist_container(CatCache, cc_next, cache_iter.cur);
- Index hashIndex;
- dlist_mutable_iter iter;
-
- if (cacheId != ccp->id)
- continue;
- /*
- * We don't bother to check whether the cache has finished
- * initialization yet; if not, there will be no entries in it so no
- * problem.
- */
+ /*
+ * Invalidate *all* CatCLists in this cache; it's too hard to tell which
+ * searches might still be correct, so just zap 'em all.
+ */
+ dlist_foreach_modify(iter, &cache->cc_lists)
+ {
+ CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
- /*
- * Invalidate *all* CatCLists in this cache; it's too hard to tell
- * which searches might still be correct, so just zap 'em all.
- */
- dlist_foreach_modify(iter, &ccp->cc_lists)
- {
- CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
+ if (cl->refcount > 0)
+ cl->dead = true;
+ else
+ CatCacheRemoveCList(cache, cl);
+ }
- if (cl->refcount > 0)
- cl->dead = true;
- else
- CatCacheRemoveCList(ccp, cl);
- }
+ /*
+ * inspect the proper hash bucket for tuple matches
+ */
+ hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+ dlist_foreach_modify(iter, &cache->cc_bucket[hashIndex])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
- /*
- * inspect the proper hash bucket for tuple matches
- */
- hashIndex = HASH_INDEX(hashValue, ccp->cc_nbuckets);
- dlist_foreach_modify(iter, &ccp->cc_bucket[hashIndex])
+ if (hashValue == ct->hash_value)
{
- CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
-
- if (hashValue == ct->hash_value)
+ if (ct->refcount > 0 ||
+ (ct->c_list && ct->c_list->refcount > 0))
{
- if (ct->refcount > 0 ||
- (ct->c_list && ct->c_list->refcount > 0))
- {
- ct->dead = true;
- /* list, if any, was marked dead above */
- Assert(ct->c_list == NULL || ct->c_list->dead);
- }
- else
- CatCacheRemoveCTup(ccp, ct);
- CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated");
+ ct->dead = true;
+ /* list, if any, was marked dead above */
+ Assert(ct->c_list == NULL || ct->c_list->dead);
+ }
+ else
+ CatCacheRemoveCTup(cache, ct);
+ CACHE1_elog(DEBUG2, "CatCacheInvalidate: invalidated");
#ifdef CATCACHE_STATS
- ccp->cc_invals++;
+ cache->cc_invals++;
#endif
- /* could be multiple matches, so keep looking! */
- }
+ /* could be multiple matches, so keep looking! */
}
- break; /* need only search this one cache */
}
}
@@ -1823,7 +1809,7 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys)
* the specified relation, find all catcaches it could be in, compute the
* correct hash value for each such catcache, and call the specified
* function to record the cache id and hash value in inval.c's lists.
- * CatalogCacheIdInvalidate will be called later, if appropriate,
+ * SysCacheInvalidate will be called later, if appropriate,
* using the recorded information.
*
* For an insert or delete, tuple is the target tuple and newtuple is NULL.
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 685bbd31743..72c8e9e0c48 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -552,7 +552,7 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
{
InvalidateCatalogSnapshot();
- CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
+ SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index edbc151f33e..066ce728775 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -1339,6 +1339,27 @@ SearchSysCacheList(int cacheId, int nkeys,
}
/*
+ * SysCacheInvalidate
+ *
+ * Invalidate entries in the specified cache, given a hash value.
+ * See CatCacheInvalidate() for more info.
+ *
+ * This routine is only quasi-public: it should only be used by inval.c.
+ */
+void
+SysCacheInvalidate(int cacheId, uint32 hashValue)
+{
+ if (cacheId < 0 || cacheId >= SysCacheSize)
+ elog(ERROR, "invalid cache ID: %d", cacheId);
+
+ /* if this cache isn't initialized yet, no need to do anything */
+ if (!PointerIsValid(SysCache[cacheId]))
+ return;
+
+ CatCacheInvalidate(SysCache[cacheId], hashValue);
+}
+
+/*
* Certain relations that do not have system caches send snapshot invalidation
* messages in lieu of catcache messages. This is for the benefit of
* GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot