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 ,
9898 uint32 hashValue , Index hashIndex ,
9999 bool 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,
481483 if (ct -> negative ||
482484 ItemPointerEquals (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+ {
485489 ct -> dead = true;
490+ /* list, if any, was marked dead above */
491+ Assert (ct -> c_list == NULL || ct -> c_list -> dead );
492+ }
486493 else
487494 CatCacheRemoveCTup (ccp , ct );
488495 CACHE1_elog (DEBUG2 , "CatalogCacheIdInvalidate: invalidated" );
@@ -606,8 +613,13 @@ ResetCatalogCache(CatCache *cache)
606613
607614 nextelt = DLGetSucc (elt );
608615
609- if (ct -> refcount > 0 )
616+ if (ct -> refcount > 0 ||
617+ (ct -> c_list && ct -> c_list -> refcount > 0 ))
618+ {
610619 ct -> dead = true;
620+ /* list, if any, was marked dead above */
621+ Assert (ct -> c_list == NULL || ct -> c_list -> dead );
622+ }
611623 else
612624 CatCacheRemoveCTup (cache , ct );
613625#ifdef CATCACHE_STATS
@@ -718,8 +730,14 @@ CatalogCacheFlushRelation(Oid relId)
718730
719731 if (tupRelid == relId )
720732 {
721- if (ct -> refcount > 0 )
733+ if (ct -> refcount > 0 ||
734+ (ct -> c_list && ct -> c_list -> refcount > 0 ))
735+ {
722736 ct -> dead = true;
737+ /* parent list must be considered dead too */
738+ if (ct -> c_list )
739+ ct -> c_list -> dead = true;
740+ }
723741 else
724742 CatCacheRemoveCTup (cache , ct );
725743#ifdef CATCACHE_STATS
@@ -1279,11 +1297,12 @@ ReleaseCatCache(HeapTuple tuple)
12791297 ct -> refcount -- ;
12801298 ResourceOwnerForgetCatCacheRef (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 ))
12871306 CatCacheRemoveCTup (ct -> my_cache , ct );
12881307}
12891308
@@ -1377,24 +1396,18 @@ SearchCatCacheList(CatCache *cache,
13771396 continue ;
13781397
13791398 /*
1380- * we found a matching list: move each of its members to the front
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 the last
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;
13951407 DLMoveToFront (& cl -> cache_elem );
13961408
13971409 /* Bump the list's refcount and return it */
1410+ ResourceOwnerEnlargeCatCacheListRefs (CurrentResourceOwner );
13981411 cl -> refcount ++ ;
13991412 ResourceOwnerRememberCatCacheListRef (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 refcounts immediately to ensure they
1429+ * We have to bump the member refcounts temporarily 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 */
14911505 ctlist = lappend (ctlist , ct );
14921506 ct -> refcount ++ ;
14931507 }
@@ -1526,11 +1540,12 @@ SearchCatCacheList(CatCache *cache,
15261540 Assert (ct -> c_list == NULL );
15271541 Assert (ct -> refcount > 0 );
15281542 ct -> 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 ))
15341549 CatCacheRemoveCTup (cache , ct );
15351550 }
15361551
@@ -1544,6 +1559,7 @@ SearchCatCacheList(CatCache *cache,
15441559 cl -> refcount = 0 ; /* for the moment */
15451560 cl -> dead = false;
15461561 cl -> ordered = ordered ;
1562+ cl -> touched = false; /* we already moved members to front */
15471563 cl -> nkeys = nkeys ;
15481564 cl -> hash_value = lHashValue ;
15491565 cl -> n_members = nmembers ;
@@ -1554,6 +1570,9 @@ SearchCatCacheList(CatCache *cache,
15541570 cl -> members [i ++ ] = ct = (CatCTup * ) lfirst (ctlist_item );
15551571 Assert (ct -> c_list == NULL );
15561572 ct -> 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 */
15581577 if (ct -> dead )
15591578 cl -> dead = true;
@@ -1575,38 +1594,22 @@ SearchCatCacheList(CatCache *cache,
15751594/*
15761595 * ReleaseCatCacheList
15771596 *
1578- * Decrement the reference counts of a catcache list.
1597+ * Decrement the reference count 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 */
15861603 Assert (list -> cl_magic == CL_MAGIC );
15871604 Assert (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-
16021605 list -> refcount -- ;
16031606 ResourceOwnerForgetCatCacheListRef (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 )
16101613 CatCacheRemoveCList (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 */
16601663 if (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/*
0 commit comments