1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +03:00

Modify AtEOXact_CatCache and AtEOXact_RelationCache to assume that the

ResourceOwner mechanism already released all reference counts for the
cache entries; therefore, we do not need to scan the catcache or relcache
at transaction end, unless we want to do it as a debugging crosscheck.
Do the crosscheck only in Assert mode.  This is the same logic we had
previously installed in AtEOXact_Buffers to avoid overhead with large
numbers of shared buffers.  I thought it'd be a good idea to do it here
too, in view of Kari Lavikka's recent report showing a real-world case
where AtEOXact_CatCache is taking a significant fraction of runtime.
This commit is contained in:
Tom Lane
2005-08-08 19:17:23 +00:00
parent be27a20123
commit 4568e0f791
4 changed files with 235 additions and 238 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.121 2005/05/06 17:24:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.122 2005/08/08 19:17:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -530,62 +530,43 @@ CreateCacheMemoryContext(void)
*
* Clean up catcaches at end of main transaction (either commit or abort)
*
* We scan the caches to reset refcounts to zero. This is of course
* necessary in the abort case, since elog() may have interrupted routines.
* In the commit case, any nonzero counts indicate failure to call
* ReleaseSysCache, so we put out a notice for debugging purposes.
* As of PostgreSQL 8.1, catcache pins should get released by the
* ResourceOwner mechanism. This routine is just a debugging
* cross-check that no pins remain.
*/
void
AtEOXact_CatCache(bool isCommit)
{
CatCache *ccp;
Dlelem *elt,
*nextelt;
/*
* First clean up CatCLists
*/
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
#ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{
for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
CatCache *ccp;
Dlelem *elt;
/* Check CatCLists */
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
{
CatCList *cl = (CatCList *) DLE_VAL(elt);
nextelt = DLGetSucc(elt);
if (cl->refcount != 0)
for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt))
{
if (isCommit)
PrintCatCacheListLeakWarning(cl);
cl->refcount = 0;
CatCList *cl = (CatCList *) DLE_VAL(elt);
Assert(cl->cl_magic == CL_MAGIC);
Assert(cl->refcount == 0);
Assert(!cl->dead);
}
/* Clean up any now-deletable dead entries */
if (cl->dead)
CatCacheRemoveCList(ccp, cl);
}
}
/*
* Now clean up tuples; we can scan them all using the global LRU list
*/
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
{
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
nextelt = DLGetSucc(elt);
if (ct->refcount != 0)
/* Check individual tuples */
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = DLGetSucc(elt))
{
if (isCommit)
PrintCatCacheLeakWarning(&ct->tuple);
ct->refcount = 0;
}
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
/* Clean up any now-deletable dead entries */
if (ct->dead)
CatCacheRemoveCTup(ct->my_cache, ct);
Assert(ct->ct_magic == CT_MAGIC);
Assert(ct->refcount == 0);
Assert(!ct->dead);
}
}
#endif
}
/*
@@ -1329,11 +1310,9 @@ SearchCatCacheList(CatCache *cache,
Dlelem *elt;
CatCList *cl;
CatCTup *ct;
List *ctlist;
List * volatile ctlist;
ListCell *ctlist_item;
int nmembers;
Relation relation;
SysScanDesc scandesc;
bool ordered;
HeapTuple ntp;
MemoryContext oldcxt;
@@ -1433,98 +1412,131 @@ SearchCatCacheList(CatCache *cache,
* List was not found in cache, so we have to build it by reading the
* relation. For each matching tuple found in the relation, use an
* existing cache entry if possible, else build a new one.
*
* We have to bump the member refcounts immediately to ensure they
* won't get dropped from the cache while loading other members.
* We use a PG_TRY block to ensure we can undo those refcounts if
* we get an error before we finish constructing the CatCList.
*/
relation = heap_open(cache->cc_reloid, AccessShareLock);
scandesc = systable_beginscan(relation,
cache->cc_indexoid,
true,
SnapshotNow,
nkeys,
cur_skey);
/* The list will be ordered iff we are doing an index scan */
ordered = (scandesc->irel != NULL);
ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
ctlist = NIL;
nmembers = 0;
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
PG_TRY();
{
uint32 hashValue;
Index hashIndex;
Relation relation;
SysScanDesc scandesc;
/*
* See if there's an entry for this tuple already.
*/
ct = NULL;
hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
relation = heap_open(cache->cc_reloid, AccessShareLock);
for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
elt;
elt = DLGetSucc(elt))
scandesc = systable_beginscan(relation,
cache->cc_indexoid,
true,
SnapshotNow,
nkeys,
cur_skey);
/* The list will be ordered iff we are doing an index scan */
ordered = (scandesc->irel != NULL);
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
{
ct = (CatCTup *) DLE_VAL(elt);
if (ct->dead || ct->negative)
continue; /* ignore dead and negative entries */
if (ct->hash_value != hashValue)
continue; /* quickly skip entry if wrong hash val */
if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
continue; /* not same tuple */
uint32 hashValue;
Index hashIndex;
/*
* Found a match, but can't use it if it belongs to another
* list already
* See if there's an entry for this tuple already.
*/
if (ct->c_list)
continue;
ct = NULL;
hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
/* Found a match, so move it to front */
DLMoveToFront(&ct->lrulist_elem);
for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
elt;
elt = DLGetSucc(elt))
{
ct = (CatCTup *) DLE_VAL(elt);
break;
if (ct->dead || ct->negative)
continue; /* ignore dead and negative entries */
if (ct->hash_value != hashValue)
continue; /* quickly skip entry if wrong hash val */
if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
continue; /* not same tuple */
/*
* Found a match, but can't use it if it belongs to another
* list already
*/
if (ct->c_list)
continue;
/* Found a match, so move it to front */
DLMoveToFront(&ct->lrulist_elem);
break;
}
if (elt == NULL)
{
/* We didn't find a usable entry, so make a new one */
ct = CatalogCacheCreateEntry(cache, ntp,
hashValue, hashIndex,
false);
}
/* Careful here: add entry to ctlist, then bump its refcount */
ctlist = lappend(ctlist, ct);
ct->refcount++;
}
if (elt == NULL)
{
/* We didn't find a usable entry, so make a new one */
ct = CatalogCacheCreateEntry(cache, ntp,
hashValue, hashIndex,
false);
}
systable_endscan(scandesc);
heap_close(relation, AccessShareLock);
/*
* We have to bump the member refcounts immediately to ensure they
* won't get dropped from the cache while loading other members.
* If we get an error before we finish constructing the CatCList
* then we will leak those reference counts. This is annoying but
* it has no real consequence beyond possibly generating some
* warning messages at the next transaction commit, so it's not
* worth fixing.
* Now we can build the CatCList entry. First we need a dummy tuple
* containing the key values...
*/
ct->refcount++;
ctlist = lappend(ctlist, ct);
nmembers++;
ntp = build_dummy_tuple(cache, nkeys, cur_skey);
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
nmembers = list_length(ctlist);
cl = (CatCList *)
palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
heap_copytuple_with_tuple(ntp, &cl->tuple);
MemoryContextSwitchTo(oldcxt);
heap_freetuple(ntp);
/*
* We are now past the last thing that could trigger an elog before
* we have finished building the CatCList and remembering it in the
* resource owner. So it's OK to fall out of the PG_TRY, and indeed
* we'd better do so before we start marking the members as belonging
* to the list.
*/
}
PG_CATCH();
{
foreach(ctlist_item, ctlist)
{
ct = (CatCTup *) lfirst(ctlist_item);
Assert(ct->c_list == NULL);
Assert(ct->refcount > 0);
ct->refcount--;
if (ct->refcount == 0
#ifndef CATCACHE_FORCE_RELEASE
&& ct->dead
#endif
)
CatCacheRemoveCTup(cache, ct);
}
systable_endscan(scandesc);
heap_close(relation, AccessShareLock);
/*
* Now we can build the CatCList entry. First we need a dummy tuple
* containing the key values...
*/
ntp = build_dummy_tuple(cache, nkeys, cur_skey);
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
heap_copytuple_with_tuple(ntp, &cl->tuple);
MemoryContextSwitchTo(oldcxt);
heap_freetuple(ntp);
PG_RE_THROW();
}
PG_END_TRY();
cl->cl_magic = CL_MAGIC;
cl->my_cache = cache;
@@ -1536,29 +1548,27 @@ SearchCatCacheList(CatCache *cache,
cl->hash_value = lHashValue;
cl->n_members = nmembers;
Assert(nmembers == list_length(ctlist));
ctlist_item = list_head(ctlist);
for (i = 0; i < nmembers; i++)
i = 0;
foreach(ctlist_item, ctlist)
{
cl->members[i] = ct = (CatCTup *) lfirst(ctlist_item);
cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
Assert(ct->c_list == NULL);
ct->c_list = cl;
/* mark list dead if any members already dead */
if (ct->dead)
cl->dead = true;
ctlist_item = lnext(ctlist_item);
}
Assert(i == nmembers);
DLAddHead(&cache->cc_lists, &cl->cache_elem);
CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
cache->cc_relname, nmembers);
/* Finally, bump the list's refcount and return it */
ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
cl->refcount++;
ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
cache->cc_relname, nmembers);
return cl;
}