88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.122 2005/08/08 19:17:22 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.123 2005/08/13 22:18:07 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -97,6 +97,7 @@ static void CatalogCacheInitializeCache(CatCache *cache);
9797static CatCTup * CatalogCacheCreateEntry (CatCache * cache ,HeapTuple ntp ,
9898uint32 hashValue ,Index hashIndex ,
9999bool negative );
100+ static void CatalogCacheCleanup (CatCTup * savect );
100101static HeapTuple build_dummy_tuple (CatCache * cache ,int nkeys ,ScanKey skeys );
101102
102103
@@ -344,6 +345,7 @@ CatCachePrintStats(void)
344345 * Unlink and delete the given cache entry
345346 *
346347 * NB: if it is a member of a CatCList, the CatCList is deleted too.
348+ * Both the cache entry and the list had better have zero refcount.
347349 */
348350static void
349351CatCacheRemoveCTup (CatCache * cache ,CatCTup * ct )
@@ -481,8 +483,13 @@ CatalogCacheIdInvalidate(int cacheId,
481483if (ct -> negative ||
482484ItemPointerEquals (pointer ,& ct -> tuple .t_self ))
483485{
484- if (ct -> refcount > 0 )
486+ if (ct -> refcount > 0 ||
487+ (ct -> c_list && ct -> c_list -> refcount > 0 ))
488+ {
485489ct -> dead = true;
490+ /* list, if any, was marked dead above */
491+ Assert (ct -> c_list == NULL || ct -> c_list -> dead );
492+ }
486493else
487494CatCacheRemoveCTup (ccp ,ct );
488495CACHE1_elog (DEBUG2 ,"CatalogCacheIdInvalidate: invalidated" );
@@ -606,8 +613,13 @@ ResetCatalogCache(CatCache *cache)
606613
607614nextelt = DLGetSucc (elt );
608615
609- if (ct -> refcount > 0 )
616+ if (ct -> refcount > 0 ||
617+ (ct -> c_list && ct -> c_list -> refcount > 0 ))
618+ {
610619ct -> dead = true;
620+ /* list, if any, was marked dead above */
621+ Assert (ct -> c_list == NULL || ct -> c_list -> dead );
622+ }
611623else
612624CatCacheRemoveCTup (cache ,ct );
613625#ifdef CATCACHE_STATS
@@ -718,8 +730,14 @@ CatalogCacheFlushRelation(Oid relId)
718730
719731if (tupRelid == relId )
720732{
721- if (ct -> refcount > 0 )
733+ if (ct -> refcount > 0 ||
734+ (ct -> c_list && ct -> c_list -> refcount > 0 ))
735+ {
722736ct -> dead = true;
737+ /* parent list must be considered dead too */
738+ if (ct -> c_list )
739+ ct -> c_list -> dead = true;
740+ }
723741else
724742CatCacheRemoveCTup (cache ,ct );
725743#ifdef CATCACHE_STATS
@@ -1279,11 +1297,12 @@ ReleaseCatCache(HeapTuple tuple)
12791297ct -> refcount -- ;
12801298ResourceOwnerForgetCatCacheRef (CurrentResourceOwner ,& ct -> tuple );
12811299
1282- if (ct -> refcount == 0
1300+ if (
12831301#ifndef CATCACHE_FORCE_RELEASE
1284- && ct -> dead
1302+ ct -> dead &&
12851303#endif
1286- )
1304+ ct -> refcount == 0 &&
1305+ (ct -> c_list == NULL || ct -> c_list -> refcount == 0 ))
12871306CatCacheRemoveCTup (ct -> my_cache ,ct );
12881307}
12891308
@@ -1377,24 +1396,18 @@ SearchCatCacheList(CatCache *cache,
13771396continue ;
13781397
13791398/*
1380- *we found a matching list:move each of its members to thefront
1381- *of the global LRU list. Also move the list itself to the front
1382- * of the cache's list-of-lists, to speed subsequent searches. (We
1383- * do not move the members to the fronts of their hashbucket
1399+ *We found a matching list:mark it as touched since thelast
1400+ *CatalogCacheCleanup() sweep. Also move the list to the front
1401+ * of the cache's list-of-lists, to speed subsequent searches.
1402+ *(We do not move the members to the fronts of their hashbucket
13841403 * lists, however, since there's no point in that unless they are
1385- * searched for individually.)Also bump the members' refcounts.
1386- * (member refcounts are NOT registered separately with the
1387- * resource owner.)
1404+ * searched for individually.)
13881405 */
1389- ResourceOwnerEnlargeCatCacheListRefs (CurrentResourceOwner );
1390- for (i = 0 ;i < cl -> n_members ;i ++ )
1391- {
1392- cl -> members [i ]-> refcount ++ ;
1393- DLMoveToFront (& cl -> members [i ]-> lrulist_elem );
1394- }
1406+ cl -> touched = true;
13951407DLMoveToFront (& cl -> cache_elem );
13961408
13971409/* Bump the list's refcount and return it */
1410+ ResourceOwnerEnlargeCatCacheListRefs (CurrentResourceOwner );
13981411cl -> refcount ++ ;
13991412ResourceOwnerRememberCatCacheListRef (CurrentResourceOwner ,cl );
14001413
@@ -1413,7 +1426,7 @@ SearchCatCacheList(CatCache *cache,
14131426 * relation. For each matching tuple found in the relation, use an
14141427 * existing cache entry if possible, else build a new one.
14151428 *
1416- * We have to bump the member refcountsimmediately to ensure they
1429+ * We have to bump the member refcountstemporarily to ensure they
14171430 * won't get dropped from the cache while loading other members.
14181431 * We use a PG_TRY block to ensure we can undo those refcounts if
14191432 * we get an error before we finish constructing the CatCList.
@@ -1488,6 +1501,7 @@ SearchCatCacheList(CatCache *cache,
14881501}
14891502
14901503/* Careful here: add entry to ctlist, then bump its refcount */
1504+ /* This way leaves state correct if lappend runs out of memory */
14911505ctlist = lappend (ctlist ,ct );
14921506ct -> refcount ++ ;
14931507}
@@ -1526,11 +1540,12 @@ SearchCatCacheList(CatCache *cache,
15261540Assert (ct -> c_list == NULL );
15271541Assert (ct -> refcount > 0 );
15281542ct -> refcount -- ;
1529- if (ct -> refcount == 0
1543+ if (
15301544#ifndef CATCACHE_FORCE_RELEASE
1531- && ct -> dead
1545+ ct -> dead &&
15321546#endif
1533- )
1547+ ct -> refcount == 0 &&
1548+ (ct -> c_list == NULL || ct -> c_list -> refcount == 0 ))
15341549CatCacheRemoveCTup (cache ,ct );
15351550}
15361551
@@ -1544,6 +1559,7 @@ SearchCatCacheList(CatCache *cache,
15441559cl -> refcount = 0 ;/* for the moment */
15451560cl -> dead = false;
15461561cl -> ordered = ordered ;
1562+ cl -> touched = false;/* we already moved members to front */
15471563cl -> nkeys = nkeys ;
15481564cl -> hash_value = lHashValue ;
15491565cl -> n_members = nmembers ;
@@ -1554,6 +1570,9 @@ SearchCatCacheList(CatCache *cache,
15541570cl -> members [i ++ ]= ct = (CatCTup * )lfirst (ctlist_item );
15551571Assert (ct -> c_list == NULL );
15561572ct -> c_list = cl ;
1573+ /* release the temporary refcount on the member */
1574+ Assert (ct -> refcount > 0 );
1575+ ct -> refcount -- ;
15571576/* mark list dead if any members already dead */
15581577if (ct -> dead )
15591578cl -> dead = true;
@@ -1575,38 +1594,22 @@ SearchCatCacheList(CatCache *cache,
15751594/*
15761595 *ReleaseCatCacheList
15771596 *
1578- *Decrement the referencecounts of a catcache list.
1597+ *Decrement the referencecount of a catcache list.
15791598 */
15801599void
15811600ReleaseCatCacheList (CatCList * list )
15821601{
1583- int i ;
1584-
15851602/* Safety checks to ensure we were handed a cache entry */
15861603Assert (list -> cl_magic == CL_MAGIC );
15871604Assert (list -> refcount > 0 );
1588-
1589- for (i = list -> n_members ;-- i >=0 ;)
1590- {
1591- CatCTup * ct = list -> members [i ];
1592-
1593- Assert (ct -> refcount > 0 );
1594-
1595- ct -> refcount -- ;
1596-
1597- if (ct -> dead )
1598- list -> dead = true;
1599- /* can't remove tuple before list is removed */
1600- }
1601-
16021605list -> refcount -- ;
16031606ResourceOwnerForgetCatCacheListRef (CurrentResourceOwner ,list );
16041607
1605- if (list -> refcount == 0
1608+ if (
16061609#ifndef CATCACHE_FORCE_RELEASE
1607- && list -> dead
1610+ list -> dead &&
16081611#endif
1609- )
1612+ list -> refcount == 0 )
16101613CatCacheRemoveCList (list -> my_cache ,list );
16111614}
16121615
@@ -1654,35 +1657,87 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
16541657
16551658/*
16561659 * If we've exceeded the desired size of the caches, try to throw away
1657- * the least recently used entry. NB: be careful not to throw away
1660+ * the least recently used entry(s) . NB: be careful not to throw away
16581661 * the newly-built entry...
16591662 */
16601663if (CacheHdr -> ch_ntup > CacheHdr -> ch_maxtup )
1661- {
1662- Dlelem * elt ,
1663- * prevelt ;
1664+ CatalogCacheCleanup (ct );
16641665
1665- for (elt = DLGetTail (& CacheHdr -> ch_lrulist );elt ;elt = prevelt )
1666- {
1667- CatCTup * oldct = (CatCTup * )DLE_VAL (elt );
1666+ return ct ;
1667+ }
1668+
1669+ /*
1670+ * CatalogCacheCleanup
1671+ *Try to reduce the size of the catcaches when they get too big
1672+ *
1673+ * savect can be NULL, or a specific CatCTup not to remove even if it
1674+ * has zero refcount.
1675+ */
1676+ static void
1677+ CatalogCacheCleanup (CatCTup * savect )
1678+ {
1679+ int tup_target ;
1680+ CatCache * ccp ;
1681+ Dlelem * elt ,
1682+ * prevelt ;
16681683
1669- prevelt = DLGetPred (elt );
1684+ /*
1685+ * Each time we have to do this, try to cut the cache size down to
1686+ * about 90% of the maximum.
1687+ */
1688+ tup_target = (CacheHdr -> ch_maxtup * 9 ) /10 ;
1689+
1690+ /*
1691+ * Our strategy for managing CatCLists is that, each time we have to
1692+ * throw away some cache entries, we first move-to-front all the members
1693+ * of CatCLists that have been touched since the last cleanup sweep.
1694+ * Then we do strict LRU elimination by individual tuples, zapping a list
1695+ * if any of its members gets zapped. Before PostgreSQL 8.1, we moved
1696+ * members to front each time their owning list was touched, which was
1697+ * arguably more fair in balancing list members against standalone tuples
1698+ * --- but the overhead for large lists was horrendous. This scheme is
1699+ * more heavily biased towards preserving lists, but that is not
1700+ * necessarily bad either.
1701+ */
1702+ for (ccp = CacheHdr -> ch_caches ;ccp ;ccp = ccp -> cc_next )
1703+ {
1704+ for (elt = DLGetHead (& ccp -> cc_lists );elt ;elt = DLGetSucc (elt ))
1705+ {
1706+ CatCList * cl = (CatCList * )DLE_VAL (elt );
16701707
1671- if (oldct -> refcount == 0 && oldct != ct )
1708+ Assert (cl -> cl_magic == CL_MAGIC );
1709+ if (cl -> touched && !cl -> dead )
16721710{
1673- CACHE2_elog (DEBUG2 ,"CatCacheCreateEntry(%s): Overflow, LRU removal" ,
1674- cache -> cc_relname );
1675- #ifdef CATCACHE_STATS
1676- oldct -> my_cache -> cc_discards ++ ;
1677- #endif
1678- CatCacheRemoveCTup (oldct -> my_cache ,oldct );
1679- if (CacheHdr -> ch_ntup <=CacheHdr -> ch_maxtup )
1680- break ;
1711+ int i ;
1712+
1713+ for (i = 0 ;i < cl -> n_members ;i ++ )
1714+ DLMoveToFront (& cl -> members [i ]-> lrulist_elem );
16811715}
1716+ cl -> touched = false;
16821717}
16831718}
16841719
1685- return ct ;
1720+ /* Now get rid of unreferenced tuples in reverse global LRU order */
1721+ for (elt = DLGetTail (& CacheHdr -> ch_lrulist );elt ;elt = prevelt )
1722+ {
1723+ CatCTup * ct = (CatCTup * )DLE_VAL (elt );
1724+
1725+ prevelt = DLGetPred (elt );
1726+
1727+ if (ct -> refcount == 0 &&
1728+ (ct -> c_list == NULL || ct -> c_list -> refcount == 0 )&&
1729+ ct != savect )
1730+ {
1731+ #ifdef CATCACHE_STATS
1732+ ct -> my_cache -> cc_discards ++ ;
1733+ #endif
1734+ CatCacheRemoveCTup (ct -> my_cache ,ct );
1735+
1736+ /* Quit when we've removed enough tuples */
1737+ if (CacheHdr -> ch_ntup <=tup_target )
1738+ break ;
1739+ }
1740+ }
16861741}
16871742
16881743/*