mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +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:
parent
be27a20123
commit
4568e0f791
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.211 2005/07/25 22:12:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.212 2005/08/08 19:17:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1549,6 +1549,9 @@ CommitTransaction(void)
|
|||||||
/* Check we've released all buffer pins */
|
/* Check we've released all buffer pins */
|
||||||
AtEOXact_Buffers(true);
|
AtEOXact_Buffers(true);
|
||||||
|
|
||||||
|
/* Clean up the relation cache */
|
||||||
|
AtEOXact_RelationCache(true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make catalog changes visible to all backends. This has to happen
|
* Make catalog changes visible to all backends. This has to happen
|
||||||
* after relcache references are dropped (see comments for
|
* after relcache references are dropped (see comments for
|
||||||
@ -1576,6 +1579,9 @@ CommitTransaction(void)
|
|||||||
RESOURCE_RELEASE_AFTER_LOCKS,
|
RESOURCE_RELEASE_AFTER_LOCKS,
|
||||||
true, true);
|
true, true);
|
||||||
|
|
||||||
|
/* Check we've released all catcache entries */
|
||||||
|
AtEOXact_CatCache(true);
|
||||||
|
|
||||||
AtEOXact_GUC(true, false);
|
AtEOXact_GUC(true, false);
|
||||||
AtEOXact_SPI(true);
|
AtEOXact_SPI(true);
|
||||||
AtEOXact_on_commit_actions(true);
|
AtEOXact_on_commit_actions(true);
|
||||||
@ -1768,6 +1774,9 @@ PrepareTransaction(void)
|
|||||||
/* Check we've released all buffer pins */
|
/* Check we've released all buffer pins */
|
||||||
AtEOXact_Buffers(true);
|
AtEOXact_Buffers(true);
|
||||||
|
|
||||||
|
/* Clean up the relation cache */
|
||||||
|
AtEOXact_RelationCache(true);
|
||||||
|
|
||||||
/* notify and flatfiles don't need a postprepare call */
|
/* notify and flatfiles don't need a postprepare call */
|
||||||
|
|
||||||
PostPrepare_Inval();
|
PostPrepare_Inval();
|
||||||
@ -1785,6 +1794,9 @@ PrepareTransaction(void)
|
|||||||
RESOURCE_RELEASE_AFTER_LOCKS,
|
RESOURCE_RELEASE_AFTER_LOCKS,
|
||||||
true, true);
|
true, true);
|
||||||
|
|
||||||
|
/* Check we've released all catcache entries */
|
||||||
|
AtEOXact_CatCache(true);
|
||||||
|
|
||||||
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
|
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
|
||||||
AtEOXact_GUC(true, false);
|
AtEOXact_GUC(true, false);
|
||||||
AtEOXact_SPI(true);
|
AtEOXact_SPI(true);
|
||||||
@ -1922,6 +1934,7 @@ AbortTransaction(void)
|
|||||||
RESOURCE_RELEASE_BEFORE_LOCKS,
|
RESOURCE_RELEASE_BEFORE_LOCKS,
|
||||||
false, true);
|
false, true);
|
||||||
AtEOXact_Buffers(false);
|
AtEOXact_Buffers(false);
|
||||||
|
AtEOXact_RelationCache(false);
|
||||||
AtEOXact_Inval(false);
|
AtEOXact_Inval(false);
|
||||||
smgrDoPendingDeletes(false);
|
smgrDoPendingDeletes(false);
|
||||||
AtEOXact_MultiXact();
|
AtEOXact_MultiXact();
|
||||||
@ -1931,6 +1944,7 @@ AbortTransaction(void)
|
|||||||
ResourceOwnerRelease(TopTransactionResourceOwner,
|
ResourceOwnerRelease(TopTransactionResourceOwner,
|
||||||
RESOURCE_RELEASE_AFTER_LOCKS,
|
RESOURCE_RELEASE_AFTER_LOCKS,
|
||||||
false, true);
|
false, true);
|
||||||
|
AtEOXact_CatCache(false);
|
||||||
|
|
||||||
AtEOXact_GUC(false, false);
|
AtEOXact_GUC(false, false);
|
||||||
AtEOXact_SPI(false);
|
AtEOXact_SPI(false);
|
||||||
|
266
src/backend/utils/cache/catcache.c
vendored
266
src/backend/utils/cache/catcache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
* 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
|
* As of PostgreSQL 8.1, catcache pins should get released by the
|
||||||
* necessary in the abort case, since elog() may have interrupted routines.
|
* ResourceOwner mechanism. This routine is just a debugging
|
||||||
* In the commit case, any nonzero counts indicate failure to call
|
* cross-check that no pins remain.
|
||||||
* ReleaseSysCache, so we put out a notice for debugging purposes.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
AtEOXact_CatCache(bool isCommit)
|
AtEOXact_CatCache(bool isCommit)
|
||||||
{
|
{
|
||||||
CatCache *ccp;
|
#ifdef USE_ASSERT_CHECKING
|
||||||
Dlelem *elt,
|
if (assert_enabled)
|
||||||
*nextelt;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First clean up CatCLists
|
|
||||||
*/
|
|
||||||
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
|
|
||||||
{
|
{
|
||||||
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);
|
for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt))
|
||||||
|
|
||||||
nextelt = DLGetSucc(elt);
|
|
||||||
|
|
||||||
if (cl->refcount != 0)
|
|
||||||
{
|
{
|
||||||
if (isCommit)
|
CatCList *cl = (CatCList *) DLE_VAL(elt);
|
||||||
PrintCatCacheListLeakWarning(cl);
|
|
||||||
cl->refcount = 0;
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/* Check individual tuples */
|
||||||
* Now clean up tuples; we can scan them all using the global LRU list
|
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = DLGetSucc(elt))
|
||||||
*/
|
|
||||||
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
|
|
||||||
{
|
|
||||||
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
|
|
||||||
|
|
||||||
nextelt = DLGetSucc(elt);
|
|
||||||
|
|
||||||
if (ct->refcount != 0)
|
|
||||||
{
|
{
|
||||||
if (isCommit)
|
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
|
||||||
PrintCatCacheLeakWarning(&ct->tuple);
|
|
||||||
ct->refcount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clean up any now-deletable dead entries */
|
Assert(ct->ct_magic == CT_MAGIC);
|
||||||
if (ct->dead)
|
Assert(ct->refcount == 0);
|
||||||
CatCacheRemoveCTup(ct->my_cache, ct);
|
Assert(!ct->dead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1329,11 +1310,9 @@ SearchCatCacheList(CatCache *cache,
|
|||||||
Dlelem *elt;
|
Dlelem *elt;
|
||||||
CatCList *cl;
|
CatCList *cl;
|
||||||
CatCTup *ct;
|
CatCTup *ct;
|
||||||
List *ctlist;
|
List * volatile ctlist;
|
||||||
ListCell *ctlist_item;
|
ListCell *ctlist_item;
|
||||||
int nmembers;
|
int nmembers;
|
||||||
Relation relation;
|
|
||||||
SysScanDesc scandesc;
|
|
||||||
bool ordered;
|
bool ordered;
|
||||||
HeapTuple ntp;
|
HeapTuple ntp;
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
@ -1433,98 +1412,131 @@ SearchCatCacheList(CatCache *cache,
|
|||||||
* List was not found in cache, so we have to build it by reading the
|
* 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
|
* relation. For each matching tuple found in the relation, use an
|
||||||
* existing cache entry if possible, else build a new one.
|
* 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);
|
ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
ctlist = NIL;
|
ctlist = NIL;
|
||||||
nmembers = 0;
|
|
||||||
|
|
||||||
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
|
PG_TRY();
|
||||||
{
|
{
|
||||||
uint32 hashValue;
|
Relation relation;
|
||||||
Index hashIndex;
|
SysScanDesc scandesc;
|
||||||
|
|
||||||
/*
|
relation = heap_open(cache->cc_reloid, AccessShareLock);
|
||||||
* See if there's an entry for this tuple already.
|
|
||||||
*/
|
|
||||||
ct = NULL;
|
|
||||||
hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
|
|
||||||
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
|
|
||||||
|
|
||||||
for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
|
scandesc = systable_beginscan(relation,
|
||||||
elt;
|
cache->cc_indexoid,
|
||||||
elt = DLGetSucc(elt))
|
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);
|
uint32 hashValue;
|
||||||
|
Index hashIndex;
|
||||||
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
|
* See if there's an entry for this tuple already.
|
||||||
* list already
|
|
||||||
*/
|
*/
|
||||||
if (ct->c_list)
|
ct = NULL;
|
||||||
continue;
|
hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
|
||||||
|
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
|
||||||
|
|
||||||
/* Found a match, so move it to front */
|
for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
|
||||||
DLMoveToFront(&ct->lrulist_elem);
|
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)
|
systable_endscan(scandesc);
|
||||||
{
|
|
||||||
/* We didn't find a usable entry, so make a new one */
|
heap_close(relation, AccessShareLock);
|
||||||
ct = CatalogCacheCreateEntry(cache, ntp,
|
|
||||||
hashValue, hashIndex,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have to bump the member refcounts immediately to ensure they
|
* Now we can build the CatCList entry. First we need a dummy tuple
|
||||||
* won't get dropped from the cache while loading other members.
|
* containing the key values...
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
ct->refcount++;
|
ntp = build_dummy_tuple(cache, nkeys, cur_skey);
|
||||||
ctlist = lappend(ctlist, ct);
|
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
nmembers++;
|
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);
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
heap_close(relation, AccessShareLock);
|
PG_END_TRY();
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
cl->cl_magic = CL_MAGIC;
|
cl->cl_magic = CL_MAGIC;
|
||||||
cl->my_cache = cache;
|
cl->my_cache = cache;
|
||||||
@ -1536,29 +1548,27 @@ SearchCatCacheList(CatCache *cache,
|
|||||||
cl->hash_value = lHashValue;
|
cl->hash_value = lHashValue;
|
||||||
cl->n_members = nmembers;
|
cl->n_members = nmembers;
|
||||||
|
|
||||||
Assert(nmembers == list_length(ctlist));
|
i = 0;
|
||||||
ctlist_item = list_head(ctlist);
|
foreach(ctlist_item, ctlist)
|
||||||
for (i = 0; i < nmembers; i++)
|
|
||||||
{
|
{
|
||||||
cl->members[i] = ct = (CatCTup *) lfirst(ctlist_item);
|
cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
|
||||||
Assert(ct->c_list == NULL);
|
Assert(ct->c_list == NULL);
|
||||||
ct->c_list = cl;
|
ct->c_list = cl;
|
||||||
/* mark list dead if any members already dead */
|
/* mark list dead if any members already dead */
|
||||||
if (ct->dead)
|
if (ct->dead)
|
||||||
cl->dead = true;
|
cl->dead = true;
|
||||||
ctlist_item = lnext(ctlist_item);
|
|
||||||
}
|
}
|
||||||
|
Assert(i == nmembers);
|
||||||
|
|
||||||
DLAddHead(&cache->cc_lists, &cl->cache_elem);
|
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 */
|
/* Finally, bump the list's refcount and return it */
|
||||||
ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
|
|
||||||
cl->refcount++;
|
cl->refcount++;
|
||||||
ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
|
ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
|
||||||
|
|
||||||
|
CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
|
||||||
|
cache->cc_relname, nmembers);
|
||||||
|
|
||||||
return cl;
|
return cl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
src/backend/utils/cache/relcache.c
vendored
105
src/backend/utils/cache/relcache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.225 2005/05/29 04:23:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.226 2005/08/08 19:17:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -122,9 +122,9 @@ static long relcacheInvalsReceived = 0L;
|
|||||||
static List *initFileRelationIds = NIL;
|
static List *initFileRelationIds = NIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This flag lets us optimize away work in AtEOSubXact_RelationCache().
|
* This flag lets us optimize away work in AtEO(Sub)Xact_RelationCache().
|
||||||
*/
|
*/
|
||||||
static bool need_eosubxact_work = false;
|
static bool need_eoxact_work = false;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1816,6 +1816,12 @@ RelationCacheInvalidate(void)
|
|||||||
* In the case of abort, we don't want to try to rebuild any invalidated
|
* In the case of abort, we don't want to try to rebuild any invalidated
|
||||||
* cache entries (since we can't safely do database accesses). Therefore
|
* cache entries (since we can't safely do database accesses). Therefore
|
||||||
* we must reset refcnts before handling pending invalidations.
|
* we must reset refcnts before handling pending invalidations.
|
||||||
|
*
|
||||||
|
* As of PostgreSQL 8.1, relcache refcnts should get released by the
|
||||||
|
* ResourceOwner mechanism. This routine just does a debugging
|
||||||
|
* cross-check that no pins remain. However, we also need to do special
|
||||||
|
* cleanup when the current transaction created any relations or made use
|
||||||
|
* of forced index lists.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
AtEOXact_RelationCache(bool isCommit)
|
AtEOXact_RelationCache(bool isCommit)
|
||||||
@ -1823,12 +1829,47 @@ AtEOXact_RelationCache(bool isCommit)
|
|||||||
HASH_SEQ_STATUS status;
|
HASH_SEQ_STATUS status;
|
||||||
RelIdCacheEnt *idhentry;
|
RelIdCacheEnt *idhentry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To speed up transaction exit, we want to avoid scanning the relcache
|
||||||
|
* unless there is actually something for this routine to do. Other
|
||||||
|
* than the debug-only Assert checks, most transactions don't create
|
||||||
|
* any work for us to do here, so we keep a static flag that gets set
|
||||||
|
* if there is anything to do. (Currently, this means either a relation
|
||||||
|
* is created in the current xact, or an index list is forced.) For
|
||||||
|
* simplicity, the flag remains set till end of top-level transaction,
|
||||||
|
* even though we could clear it at subtransaction end in some cases.
|
||||||
|
*/
|
||||||
|
if (!need_eoxact_work
|
||||||
|
#ifdef USE_ASSERT_CHECKING
|
||||||
|
&& !assert_enabled
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
hash_seq_init(&status, RelationIdCache);
|
hash_seq_init(&status, RelationIdCache);
|
||||||
|
|
||||||
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
|
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
|
||||||
{
|
{
|
||||||
Relation relation = idhentry->reldesc;
|
Relation relation = idhentry->reldesc;
|
||||||
int expected_refcnt;
|
|
||||||
|
/*
|
||||||
|
* The relcache entry's ref count should be back to its normal
|
||||||
|
* not-in-a-transaction state: 0 unless it's nailed in cache.
|
||||||
|
*
|
||||||
|
* In bootstrap mode, this is NOT true, so don't check it ---
|
||||||
|
* the bootstrap code expects relations to stay open across
|
||||||
|
* start/commit transaction calls. (That seems bogus, but it's
|
||||||
|
* not worth fixing.)
|
||||||
|
*/
|
||||||
|
#ifdef USE_ASSERT_CHECKING
|
||||||
|
if (!IsBootstrapProcessingMode())
|
||||||
|
{
|
||||||
|
int expected_refcnt;
|
||||||
|
|
||||||
|
expected_refcnt = relation->rd_isnailed ? 1 : 0;
|
||||||
|
Assert(relation->rd_refcnt == expected_refcnt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is it a relation created in the current transaction?
|
* Is it a relation created in the current transaction?
|
||||||
@ -1851,40 +1892,6 @@ AtEOXact_RelationCache(bool isCommit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* During transaction abort, we must also reset relcache entry ref
|
|
||||||
* counts to their normal not-in-a-transaction state. A ref count
|
|
||||||
* may be too high because some routine was exited by ereport()
|
|
||||||
* between incrementing and decrementing the count.
|
|
||||||
*
|
|
||||||
* During commit, we should not have to do this, but it's still
|
|
||||||
* useful to check that the counts are correct to catch missed
|
|
||||||
* relcache closes.
|
|
||||||
*
|
|
||||||
* In bootstrap mode, do NOT reset the refcnt nor complain that it's
|
|
||||||
* nonzero --- the bootstrap code expects relations to stay open
|
|
||||||
* across start/commit transaction calls. (That seems bogus, but
|
|
||||||
* it's not worth fixing.)
|
|
||||||
*/
|
|
||||||
expected_refcnt = relation->rd_isnailed ? 1 : 0;
|
|
||||||
|
|
||||||
if (isCommit)
|
|
||||||
{
|
|
||||||
if (relation->rd_refcnt != expected_refcnt &&
|
|
||||||
!IsBootstrapProcessingMode())
|
|
||||||
{
|
|
||||||
elog(WARNING, "relcache reference leak: relation \"%s\" has refcnt %d instead of %d",
|
|
||||||
RelationGetRelationName(relation),
|
|
||||||
relation->rd_refcnt, expected_refcnt);
|
|
||||||
relation->rd_refcnt = expected_refcnt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* abort case, just reset it quietly */
|
|
||||||
relation->rd_refcnt = expected_refcnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flush any temporary index list.
|
* Flush any temporary index list.
|
||||||
*/
|
*/
|
||||||
@ -1896,8 +1903,8 @@ AtEOXact_RelationCache(bool isCommit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Once done with the transaction, we can reset need_eosubxact_work */
|
/* Once done with the transaction, we can reset need_eoxact_work */
|
||||||
need_eosubxact_work = false;
|
need_eoxact_work = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1915,18 +1922,10 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
|
|||||||
RelIdCacheEnt *idhentry;
|
RelIdCacheEnt *idhentry;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the majority of subtransactions there is not anything for this
|
* Skip the relcache scan if nothing to do --- see notes for
|
||||||
* routine to do, and since there are usually many entries in the
|
* AtEOXact_RelationCache.
|
||||||
* relcache, uselessly scanning the cache represents a surprisingly
|
|
||||||
* large fraction of the subtransaction entry/exit overhead. To avoid
|
|
||||||
* this, we keep a static flag that must be set whenever a condition
|
|
||||||
* is created that requires subtransaction-end work. (Currently, this
|
|
||||||
* means either a relation is created in the current xact, or an index
|
|
||||||
* list is forced.) For simplicity, the flag remains set till end of
|
|
||||||
* top-level transaction, even though we could clear it earlier in some
|
|
||||||
* cases.
|
|
||||||
*/
|
*/
|
||||||
if (!need_eosubxact_work)
|
if (!need_eoxact_work)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hash_seq_init(&status, RelationIdCache);
|
hash_seq_init(&status, RelationIdCache);
|
||||||
@ -2032,7 +2031,7 @@ RelationBuildLocalRelation(const char *relname,
|
|||||||
rel->rd_createSubid = GetCurrentSubTransactionId();
|
rel->rd_createSubid = GetCurrentSubTransactionId();
|
||||||
|
|
||||||
/* must flag that we have rels created in this transaction */
|
/* must flag that we have rels created in this transaction */
|
||||||
need_eosubxact_work = true;
|
need_eoxact_work = true;
|
||||||
|
|
||||||
/* is it a temporary relation? */
|
/* is it a temporary relation? */
|
||||||
rel->rd_istemp = isTempNamespace(relnamespace);
|
rel->rd_istemp = isTempNamespace(relnamespace);
|
||||||
@ -2626,7 +2625,7 @@ RelationSetIndexList(Relation relation, List *indexIds)
|
|||||||
relation->rd_indexlist = indexIds;
|
relation->rd_indexlist = indexIds;
|
||||||
relation->rd_indexvalid = 2; /* mark list as forced */
|
relation->rd_indexvalid = 2; /* mark list as forced */
|
||||||
/* must flag that we have a forced index list */
|
/* must flag that we have a forced index list */
|
||||||
need_eosubxact_work = true;
|
need_eoxact_work = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.12 2005/04/06 04:34:22 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.13 2005/08/08 19:17:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -213,32 +213,19 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
|||||||
ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
|
ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release relcache references */
|
/*
|
||||||
if (isTopLevel)
|
* Release relcache references. Note that RelationClose will
|
||||||
|
* remove the relref entry from my list, so I just have to
|
||||||
|
* iterate till there are none.
|
||||||
|
*
|
||||||
|
* As with buffer pins, warn if any are left at commit time,
|
||||||
|
* and release back-to-front for speed.
|
||||||
|
*/
|
||||||
|
while (owner->nrelrefs > 0)
|
||||||
{
|
{
|
||||||
/*
|
if (isCommit)
|
||||||
* For a top-level xact we are going to release all
|
PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
|
||||||
* references, so just do a single relcache call at the top of
|
RelationClose(owner->relrefs[owner->nrelrefs - 1]);
|
||||||
* the recursion.
|
|
||||||
*/
|
|
||||||
if (owner == TopTransactionResourceOwner)
|
|
||||||
AtEOXact_RelationCache(isCommit);
|
|
||||||
/* Mark object as owning no relrefs, just for sanity */
|
|
||||||
owner->nrelrefs = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Release relcache refs retail. Note that RelationClose will
|
|
||||||
* remove the relref entry from my list, so I just have to
|
|
||||||
* iterate till there are none.
|
|
||||||
*/
|
|
||||||
while (owner->nrelrefs > 0)
|
|
||||||
{
|
|
||||||
if (isCommit)
|
|
||||||
PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
|
|
||||||
RelationClose(owner->relrefs[owner->nrelrefs - 1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (phase == RESOURCE_RELEASE_LOCKS)
|
else if (phase == RESOURCE_RELEASE_LOCKS)
|
||||||
@ -269,40 +256,27 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
|||||||
}
|
}
|
||||||
else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
|
else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
|
||||||
{
|
{
|
||||||
/* Release catcache references */
|
/*
|
||||||
if (isTopLevel)
|
* Release catcache references. Note that ReleaseCatCache
|
||||||
|
* will remove the catref entry from my list, so I just have
|
||||||
|
* to iterate till there are none. Ditto for catcache lists.
|
||||||
|
*
|
||||||
|
* As with buffer pins, warn if any are left at commit time,
|
||||||
|
* and release back-to-front for speed.
|
||||||
|
*/
|
||||||
|
while (owner->ncatrefs > 0)
|
||||||
{
|
{
|
||||||
/*
|
if (isCommit)
|
||||||
* For a top-level xact we are going to release all
|
PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
|
||||||
* references, so just do a single catcache call at the top of
|
ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
|
||||||
* the recursion.
|
|
||||||
*/
|
|
||||||
if (owner == TopTransactionResourceOwner)
|
|
||||||
AtEOXact_CatCache(isCommit);
|
|
||||||
/* Mark object as owning no catrefs, just for sanity */
|
|
||||||
owner->ncatrefs = 0;
|
|
||||||
owner->ncatlistrefs = 0;
|
|
||||||
}
|
}
|
||||||
else
|
while (owner->ncatlistrefs > 0)
|
||||||
{
|
{
|
||||||
/*
|
if (isCommit)
|
||||||
* Release catcache refs retail. Note that ReleaseCatCache
|
PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
||||||
* will remove the catref entry from my list, so I just have
|
ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
||||||
* to iterate till there are none. Ditto for catcache lists.
|
|
||||||
*/
|
|
||||||
while (owner->ncatrefs > 0)
|
|
||||||
{
|
|
||||||
if (isCommit)
|
|
||||||
PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
|
|
||||||
ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
|
|
||||||
}
|
|
||||||
while (owner->ncatlistrefs > 0)
|
|
||||||
{
|
|
||||||
if (isCommit)
|
|
||||||
PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
|
||||||
ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean up index scans too */
|
/* Clean up index scans too */
|
||||||
ReleaseResources_gist();
|
ReleaseResources_gist();
|
||||||
ReleaseResources_hash();
|
ReleaseResources_hash();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user