mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
Invent ResourceOwner mechanism as per my recent proposal, and use it to
keep track of portal-related resources separately from transaction-related resources. This allows cursors to work in a somewhat sane fashion with nested transactions. For now, cursor behavior is non-subtransactional, that is a cursor's state does not roll back if you abort a subtransaction that fetched from the cursor. We might want to change that later.
This commit is contained in:
232
src/backend/utils/cache/catcache.c
vendored
232
src/backend/utils/cache/catcache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.113 2004/07/01 00:51:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.114 2004/07/17 03:29:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/catcache.h"
|
||||
#include "utils/relcache.h"
|
||||
#include "utils/resowner.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
@@ -360,8 +361,6 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
|
||||
/* free associated tuple data */
|
||||
if (ct->tuple.t_data != NULL)
|
||||
pfree(ct->tuple.t_data);
|
||||
if (ct->prev_refcount != NULL)
|
||||
pfree(ct->prev_refcount);
|
||||
pfree(ct);
|
||||
|
||||
--cache->cc_ntup;
|
||||
@@ -396,8 +395,6 @@ CatCacheRemoveCList(CatCache *cache, CatCList *cl)
|
||||
/* free associated tuple data */
|
||||
if (cl->tuple.t_data != NULL)
|
||||
pfree(cl->tuple.t_data);
|
||||
if (cl->prev_refcount != NULL)
|
||||
pfree(cl->prev_refcount);
|
||||
pfree(cl);
|
||||
}
|
||||
|
||||
@@ -531,7 +528,7 @@ CreateCacheMemoryContext(void)
|
||||
/*
|
||||
* AtEOXact_CatCache
|
||||
*
|
||||
* Clean up catcaches at end of 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
|
||||
* necessary in the abort case, since elog() may have interrupted routines.
|
||||
@@ -564,13 +561,6 @@ AtEOXact_CatCache(bool isCommit)
|
||||
cl->refcount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the refcount stack. Drop the item count to zero,
|
||||
* but don't deallocate the stack itself, so it can be used by
|
||||
* future subtransactions.
|
||||
*/
|
||||
cl->numpushes = 0;
|
||||
|
||||
/* Clean up any now-deletable dead entries */
|
||||
if (cl->dead)
|
||||
CatCacheRemoveCList(ccp, cl);
|
||||
@@ -596,174 +586,12 @@ AtEOXact_CatCache(bool isCommit)
|
||||
ct->refcount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the refcount stack. Drop the item count to zero,
|
||||
* but don't deallocate the stack itself, so it can be used by
|
||||
* future subtransactions.
|
||||
*/
|
||||
ct->numpushes = 0;
|
||||
|
||||
/* Clean up any now-deletable dead entries */
|
||||
if (ct->dead)
|
||||
CatCacheRemoveCTup(ct->my_cache, ct);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AtSubStart_CatCache
|
||||
*
|
||||
* Saves reference counts of each entry at subtransaction start so they
|
||||
* can be restored if the subtransaction later aborts.
|
||||
*/
|
||||
void
|
||||
AtSubStart_CatCache(void)
|
||||
{
|
||||
CatCache *ccp;
|
||||
Dlelem *elt,
|
||||
*nextelt;
|
||||
MemoryContext old_cxt;
|
||||
|
||||
|
||||
old_cxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||
|
||||
/*
|
||||
* Prepare CLists
|
||||
*/
|
||||
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
|
||||
{
|
||||
for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
|
||||
{
|
||||
CatCList *cl = (CatCList *) DLE_VAL(elt);
|
||||
|
||||
nextelt = DLGetSucc(elt);
|
||||
|
||||
if (cl->numpushes == cl->numalloc)
|
||||
{
|
||||
if (cl->numalloc == 0)
|
||||
{
|
||||
cl->numalloc = 8;
|
||||
cl->prev_refcount = palloc(sizeof(int) * cl->numalloc);
|
||||
}
|
||||
else
|
||||
{
|
||||
cl->numalloc *= 2;
|
||||
cl->prev_refcount = repalloc(cl->prev_refcount, cl->numalloc * sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
cl->prev_refcount[cl->numpushes++] = cl->refcount;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare CTuples
|
||||
*/
|
||||
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
|
||||
{
|
||||
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
|
||||
|
||||
nextelt = DLGetSucc(elt);
|
||||
|
||||
if (ct->numpushes == ct->numalloc)
|
||||
{
|
||||
if (ct->numalloc == 0)
|
||||
{
|
||||
ct->numalloc = 8;
|
||||
ct->prev_refcount = palloc(sizeof(int) * ct->numalloc);
|
||||
}
|
||||
else
|
||||
{
|
||||
ct->numalloc *= 2;
|
||||
ct->prev_refcount = repalloc(ct->prev_refcount, sizeof(int) * ct->numalloc);
|
||||
}
|
||||
}
|
||||
|
||||
ct->prev_refcount[ct->numpushes++] = ct->refcount;
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(old_cxt);
|
||||
}
|
||||
|
||||
void
|
||||
AtEOSubXact_CatCache(bool isCommit)
|
||||
{
|
||||
CatCache *ccp;
|
||||
Dlelem *elt,
|
||||
*nextelt;
|
||||
|
||||
/*
|
||||
* Restore CLists
|
||||
*/
|
||||
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
|
||||
{
|
||||
for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
|
||||
{
|
||||
CatCList *cl = (CatCList *) DLE_VAL(elt);
|
||||
|
||||
nextelt = DLGetSucc(elt);
|
||||
|
||||
/*
|
||||
* During commit, check whether the count is what
|
||||
* we expect.
|
||||
*/
|
||||
if (isCommit)
|
||||
{
|
||||
int expected_refcount;
|
||||
if (cl->numpushes > 0)
|
||||
expected_refcount = cl->prev_refcount[cl->numpushes - 1];
|
||||
else
|
||||
expected_refcount = 0;
|
||||
|
||||
if (cl->refcount != expected_refcount)
|
||||
elog(WARNING, "catcache reference leak");
|
||||
}
|
||||
|
||||
/*
|
||||
* During abort we have to restore the original count;
|
||||
* during commit, we have to restore in case of a leak,
|
||||
* and it won't harm if this is the expected count.
|
||||
*/
|
||||
if (cl->numpushes > 0)
|
||||
cl->refcount = cl->prev_refcount[--cl->numpushes];
|
||||
else
|
||||
cl->refcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare CTuples
|
||||
*/
|
||||
for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
|
||||
{
|
||||
CatCTup *ct = (CatCTup *) DLE_VAL(elt);
|
||||
|
||||
nextelt = DLGetSucc(elt);
|
||||
|
||||
if (isCommit)
|
||||
{
|
||||
int expected_refcount;
|
||||
|
||||
if (ct->numpushes > 0)
|
||||
expected_refcount = ct->prev_refcount[ct->numpushes - 1];
|
||||
else
|
||||
expected_refcount = 0;
|
||||
|
||||
if (ct->refcount != expected_refcount)
|
||||
elog(WARNING, "catcache reference leak");
|
||||
}
|
||||
|
||||
/*
|
||||
* During abort we have to restore the original count;
|
||||
* during commit, we have to restore in case of a leak,
|
||||
* and it won't harm if this is the expected count.
|
||||
*/
|
||||
if (ct->numpushes > 0)
|
||||
ct->refcount = ct->prev_refcount[--ct->numpushes];
|
||||
else
|
||||
ct->refcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ResetCatalogCache
|
||||
*
|
||||
@@ -1334,7 +1162,9 @@ SearchCatCache(CatCache *cache,
|
||||
*/
|
||||
if (!ct->negative)
|
||||
{
|
||||
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
|
||||
ct->refcount++;
|
||||
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
|
||||
|
||||
CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
|
||||
cache->cc_relname, hashIndex);
|
||||
@@ -1389,6 +1219,10 @@ SearchCatCache(CatCache *cache,
|
||||
ct = CatalogCacheCreateEntry(cache, ntp,
|
||||
hashValue, hashIndex,
|
||||
false);
|
||||
/* immediately set the refcount to 1 */
|
||||
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
|
||||
ct->refcount++;
|
||||
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
|
||||
break; /* assume only one match */
|
||||
}
|
||||
|
||||
@@ -1415,10 +1249,9 @@ SearchCatCache(CatCache *cache,
|
||||
cache->cc_relname, hashIndex);
|
||||
|
||||
/*
|
||||
* We are not returning the new entry to the caller, so reset its
|
||||
* refcount.
|
||||
* We are not returning the negative entry to the caller, so leave
|
||||
* its refcount zero.
|
||||
*/
|
||||
ct->refcount = 0; /* negative entries never have refs */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1457,6 +1290,7 @@ ReleaseCatCache(HeapTuple tuple)
|
||||
Assert(ct->refcount > 0);
|
||||
|
||||
ct->refcount--;
|
||||
ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple);
|
||||
|
||||
if (ct->refcount == 0
|
||||
#ifndef CATCACHE_FORCE_RELEASE
|
||||
@@ -1564,7 +1398,10 @@ SearchCatCacheList(CatCache *cache,
|
||||
* do not move the members to the fronts of their hashbucket
|
||||
* lists, however, since there's no point in that unless they are
|
||||
* searched for individually.) Also bump the members' refcounts.
|
||||
* (member refcounts are NOT registered separately with the
|
||||
* resource owner.)
|
||||
*/
|
||||
ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
|
||||
for (i = 0; i < cl->n_members; i++)
|
||||
{
|
||||
cl->members[i]->refcount++;
|
||||
@@ -1574,6 +1411,7 @@ SearchCatCacheList(CatCache *cache,
|
||||
|
||||
/* Bump the list's refcount and return it */
|
||||
cl->refcount++;
|
||||
ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
|
||||
|
||||
CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list",
|
||||
cache->cc_relname);
|
||||
@@ -1639,9 +1477,7 @@ SearchCatCacheList(CatCache *cache,
|
||||
if (ct->c_list)
|
||||
continue;
|
||||
|
||||
/* Found a match, so bump its refcount and move to front */
|
||||
ct->refcount++;
|
||||
|
||||
/* Found a match, so move it to front */
|
||||
DLMoveToFront(&ct->lrulist_elem);
|
||||
|
||||
break;
|
||||
@@ -1655,6 +1491,16 @@ SearchCatCacheList(CatCache *cache,
|
||||
false);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
ct->refcount++;
|
||||
ctlist = lcons(ct, ctlist);
|
||||
nmembers++;
|
||||
}
|
||||
@@ -1677,10 +1523,7 @@ SearchCatCacheList(CatCache *cache,
|
||||
cl->cl_magic = CL_MAGIC;
|
||||
cl->my_cache = cache;
|
||||
DLInitElem(&cl->cache_elem, (void *) cl);
|
||||
cl->refcount = 1; /* count this first reference */
|
||||
cl->prev_refcount = NULL;
|
||||
cl->numpushes = 0;
|
||||
cl->numalloc = 0;
|
||||
cl->refcount = 0; /* for the moment */
|
||||
cl->dead = false;
|
||||
cl->ordered = ordered;
|
||||
cl->nkeys = nkeys;
|
||||
@@ -1704,6 +1547,11 @@ SearchCatCacheList(CatCache *cache,
|
||||
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);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
@@ -1735,6 +1583,7 @@ ReleaseCatCacheList(CatCList *list)
|
||||
}
|
||||
|
||||
list->refcount--;
|
||||
ResourceOwnerForgetCatCacheListRef(CurrentResourceOwner, list);
|
||||
|
||||
if (list->refcount == 0
|
||||
#ifndef CATCACHE_FORCE_RELEASE
|
||||
@@ -1748,7 +1597,7 @@ ReleaseCatCacheList(CatCList *list)
|
||||
/*
|
||||
* CatalogCacheCreateEntry
|
||||
* Create a new CatCTup entry, copying the given HeapTuple and other
|
||||
* supplied data into it. The new entry is given refcount 1.
|
||||
* supplied data into it. The new entry initially has refcount 0.
|
||||
*/
|
||||
static CatCTup *
|
||||
CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
|
||||
@@ -1775,13 +1624,10 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
|
||||
DLInitElem(&ct->lrulist_elem, (void *) ct);
|
||||
DLInitElem(&ct->cache_elem, (void *) ct);
|
||||
ct->c_list = NULL;
|
||||
ct->refcount = 1; /* count this first reference */
|
||||
ct->refcount = 0; /* for the moment */
|
||||
ct->dead = false;
|
||||
ct->negative = negative;
|
||||
ct->hash_value = hashValue;
|
||||
ct->prev_refcount = NULL;
|
||||
ct->numpushes = 0;
|
||||
ct->numalloc = 0;
|
||||
|
||||
DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem);
|
||||
DLAddHead(&cache->cc_bucket[hashIndex], &ct->cache_elem);
|
||||
@@ -1791,8 +1637,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
|
||||
|
||||
/*
|
||||
* If we've exceeded the desired size of the caches, try to throw away
|
||||
* the least recently used entry. NB: the newly-built entry cannot
|
||||
* get thrown away here, because it has positive refcount.
|
||||
* the least recently used entry. NB: be careful not to throw away
|
||||
* the newly-built entry...
|
||||
*/
|
||||
if (CacheHdr->ch_ntup > CacheHdr->ch_maxtup)
|
||||
{
|
||||
@@ -1805,7 +1651,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
|
||||
|
||||
prevelt = DLGetPred(elt);
|
||||
|
||||
if (oldct->refcount == 0)
|
||||
if (oldct->refcount == 0 && oldct != ct)
|
||||
{
|
||||
CACHE2_elog(DEBUG2, "CatCacheCreateEntry(%s): Overflow, LRU removal",
|
||||
cache->cc_relname);
|
||||
|
Reference in New Issue
Block a user