diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 8bd57f3..13ccf1d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1661,6 +1661,66 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-catalog-cache-prune-min-age" xreflabel="catalog_cache_prune_min_age">
+      <term><varname>catalog_cache_prune_min_age</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>catalog_cache_prune_min_age</varname> configuration
+       parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the minimum amount of unused time in seconds at which a
+        system catalog cache entry is removed. -1 indicates that this feature
+        is disabled at all. The value defaults to 300 seconds (<literal>5
+        minutes</literal>). The catalog cache entries that are not used for
+        the duration can be removed to prevent it from being filled up with
+        useless entries. This behaviour is muted until the size of a catalog
+        cache exceeds <xref linkend="guc-catalog-cache-memory-target"/>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-catalog-cache-memory-target" xreflabel="catalog_cache_memory_target">
+      <term><varname>catalog_cache_memory_target</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>catalog_cache_memory_target</varname> configuration
+       parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the maximum amount of memory to which a system catalog cache
+        can expand without pruning in kilobytes. The value defaults to 0,
+        indicating that age-based pruning is always considered. After
+        exceeding this size, catalog cache starts pruning according to
+        <xref linkend="guc-catalog-cache-prune-min-age"/>. If you need to keep
+        certain amount of catalog cache entries with intermittent usage, try
+        increase this setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-catalog-cache-max-size" xreflabel="catalog_cache_max_size">
+      <term><varname>catalog_cache_max_size</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>catalog_cache_max_size</varname> configuration
+       parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the maximum total amount of memory allowed for all system
+        catalog caches in kilobytes. The value defaults to 0, indicating that
+        pruning by this parameter is disabled at all. After the amount of
+        memory used by all catalog caches exceeds this size, a new cache entry
+        creation will remove one or more not-recently-used cache entries. This
+        means frequent creation of new cache entry may lead to a slight
+        slowdown of queries.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-max-stack-depth" xreflabel="max_stack_depth">
       <term><varname>max_stack_depth</varname> (<type>integer</type>)
       <indexterm>
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8b4d94c..4e6505e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -71,6 +71,7 @@
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
+#include "utils/catcache.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
@@ -2584,6 +2585,7 @@ start_xact_command(void)
 	 * not desired, the timeout has to be disabled explicitly.
 	 */
 	enable_statement_timeout();
+	SetCatCacheClock(GetCurrentStatementStartTimestamp());
 }
 
 static void
@@ -3159,6 +3161,9 @@ ProcessInterrupts(void)
 
 	if (ParallelMessagePending)
 		HandleParallelMessages();
+
+	if (CatcacheClockTimeoutPending)
+		CatcacheClockTimeoutPending = false;
 }
 
 
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 78dd571..2b0806f 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -39,6 +39,7 @@
 #include "utils/rel.h"
 #include "utils/resowner_private.h"
 #include "utils/syscache.h"
+#include "utils/timeout.h"
 
 
  /* #define CACHEDEBUG */	/* turns DEBUG elogs on */
@@ -61,9 +62,36 @@
 #define CACHE_elog(...)
 #endif
 
+/* GUC variable to define the minimum age of entries that will be considered to
+ * be evicted in seconds. This variable is shared among various cache
+ * mechanisms.
+ */
+int catalog_cache_prune_min_age = 300;
+
+/*
+ * GUC variable to define the minimum size of hash to cosider entry eviction.
+ * This variable is shared among various cache mechanisms.
+ */
+int catalog_cache_memory_target = 0;
+
+/*
+ * GUC for limit by the number of entries. Entries are removed when the number
+ * of them goes above catalog_cache_max_size in kilobytes
+ */
+int catalog_cache_max_size = 0;
+
+/*
+ * Minimum interval between two success move of a cache entry in LRU list,
+ * in microseconds.
+ */
+#define MIN_LRU_UPDATE_INTERVAL 100000	/* 100ms */
+
 /* Cache management header --- pointer is NULL until created */
 static CatCacheHeader *CacheHdr = NULL;
 
+/* Clock used to record the last accessed time of a catcache record. */
+TimestampTz	catcacheclock = 0;
+
 static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
 					   int nkeys,
 					   Datum v1, Datum v2,
@@ -469,6 +497,7 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 
 	/* delink from linked list */
 	dlist_delete(&ct->cache_elem);
+	dlist_delete(&ct->lru_node);
 
 	/*
 	 * Free keys when we're dealing with a negative entry, normal entries just
@@ -478,6 +507,9 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 		CatCacheFreeKeys(cache->cc_tupdesc, cache->cc_nkeys,
 						 cache->cc_keyno, ct->keys);
 
+	cache->cc_memusage -= ct->size;
+	CacheHdr->ch_global_size -= ct->size;
+
 	pfree(ct);
 
 	--cache->cc_ntup;
@@ -767,6 +799,7 @@ InitCatCache(int id,
 	MemoryContext oldcxt;
 	size_t		sz;
 	int			i;
+	size_t		base_size;
 
 	/*
 	 * nbuckets is the initial number of hash buckets to use in this catcache.
@@ -798,6 +831,8 @@ InitCatCache(int id,
 		CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));
 		slist_init(&CacheHdr->ch_caches);
 		CacheHdr->ch_ntup = 0;
+		dlist_init(&CacheHdr->ch_lru_list);
+		CacheHdr->ch_global_size = 0;
 #ifdef CATCACHE_STATS
 		/* set up to dump stats at backend exit */
 		on_proc_exit(CatCachePrintStats, 0);
@@ -809,8 +844,12 @@ InitCatCache(int id,
 	 *
 	 * Note: we rely on zeroing to initialize all the dlist headers correctly
 	 */
+	base_size = MemoryContextGetUsedspace(CacheMemoryContext);
 	sz = sizeof(CatCache) + PG_CACHE_LINE_SIZE;
 	cp = (CatCache *) CACHELINEALIGN(palloc0(sz));
+	cp->cc_head_alloc_size =
+		MemoryContextGetUsedspace(CacheMemoryContext) - base_size;
+
 	cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));
 
 	/*
@@ -830,6 +869,11 @@ InitCatCache(int id,
 	for (i = 0; i < nkeys; ++i)
 		cp->cc_keyno[i] = key[i];
 
+	/* cc_head_alloc_size + consumed size for cc_bucket */
+	cp->cc_memusage =
+		MemoryContextGetUsedspace(CacheMemoryContext) - base_size;
+	CacheHdr->ch_global_size += cp->cc_memusage;
+
 	/*
 	 * new cache is initialized as far as we can go for now. print some
 	 * debugging information, if appropriate.
@@ -846,9 +890,168 @@ InitCatCache(int id,
 	 */
 	MemoryContextSwitchTo(oldcxt);
 
+	/* initialize catcache reference clock if haven't done yet */
+	if (catcacheclock == 0)
+		catcacheclock = GetCurrentTimestamp();
+
 	return cp;
 }
 
+/* Helper function for CatCacheCleanupOldEntries */
+static bool
+CatCacheCheckByAge(CatCache *cp, CatCTup *ct, bool *prune_by_age)
+{
+	long	entry_age;
+	int		us;
+
+	if (!prune_by_age || ct->my_cache != cp)
+		return false;
+
+	/*
+	 * Calculate the duration from the time of the last access to the
+	 * "current" time. Since catcacheclock is not advanced within a
+	 * transaction, the entries that are accessed within the current
+	 * transaction won't be pruned.
+	 */
+	TimestampDifference(ct->lastaccess, catcacheclock, &entry_age, &us);
+
+	/* prune by age is done */
+	if (entry_age < catalog_cache_prune_min_age)
+	{
+		*prune_by_age = false;
+		return false;
+	}
+
+	/*
+	 * Entries that are not accessed after last pruning are removed in
+	 * that seconds, and that has been accessed several times are
+	 * removed after leaving alone for up to three times of the
+	 * duration. We don't try shrink buckets since pruning effectively
+	 * caps catcache expansion in the long term.
+	 */
+	if (ct->naccess > 0)
+	{
+		ct->naccess--;
+		return false;
+	}
+	else
+		return true;
+}
+
+/* Helper function for CatCacheCleanupOldEntries */
+static bool
+CatCacheCheckBySize(CatCTup *ct, bool *prune_by_size)
+{
+	if (CacheHdr->ch_global_size >= (Size) catalog_cache_max_size * 1024)
+		return true;
+	else
+	{
+		*prune_by_size = false;
+		return false; /* we're satisfied */
+	}
+}
+
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries can be left alone for several reasons. We remove them if
+ * they are not accessed for a certain time to prevent catcache from
+ * bloating. The eviction is performed with the similar algorithm with buffer
+ * eviction using access counter. Entries that are accessed several times can
+ * live longer than those that have had less access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+	static TimestampTz prev_warn_emit = 0;
+	int			nremoved = 0;
+	int			nelems_before = cp->cc_ntup;
+	Size		size_before = CacheHdr->ch_global_size;
+	bool		prune_by_age = false;
+	bool		prune_by_size = false;
+	dlist_mutable_iter	iter;
+
+	if (catalog_cache_prune_min_age >= 0 &&
+		cp->cc_memusage > (Size) catalog_cache_memory_target * 1024L)
+		prune_by_age = true;
+
+	if (catalog_cache_max_size > 0 &&
+		CacheHdr->ch_global_size >= (Size) catalog_cache_max_size * 1024)
+		prune_by_size = true;
+
+	/* Return immediately if no pruning is wanted */
+	if (!prune_by_age && !prune_by_size)
+		return false;
+
+	/* Scan over LRU to find entries to remove */
+	dlist_foreach_modify(iter, &CacheHdr->ch_lru_list)
+	{
+		CatCTup    *ct = dlist_container(CatCTup, lru_node, iter.cur);
+		bool		remove_this = false;
+
+		/* We don't remove referenced entry */
+		if (ct->refcount != 0 ||
+			(ct->c_list && ct->c_list->refcount != 0))
+			continue;
+
+		/* check against age. prune within this cache */
+		remove_this = CatCacheCheckByAge(cp, ct, &prune_by_age);
+
+		/* check against global size. removes from all cache */
+		if (prune_by_size && !remove_this)
+			remove_this = CatCacheCheckBySize(ct, &prune_by_size);
+
+		/* exit immediately if all finished */
+		if (!prune_by_age && !prune_by_size)
+			break;
+
+		if (!remove_this)
+			continue;
+
+		/* do the work */
+		CatCacheRemoveCTup(ct->my_cache, ct);
+		nremoved++;
+	}
+
+	if (nremoved > 0)
+		elog(DEBUG1, "pruning catalog cache id=%d for %s: removed entiries %d / %d"
+			 ", removed size (kB) %d / %d",
+			 cp->id, cp->cc_relname, nremoved, nelems_before,
+			 (int) (size_before / 1024), (int) (CacheHdr->ch_global_size / 1024));
+
+	/*
+	 * If prune_by_size still remains true, we couldn't prune cache.
+	 * Warn of too small setting of catalog_cache_max_size. Take 5 seconds
+	 * between messages, using statement start timestamp to avoid frequent
+	 * gettimeofday().
+	 */
+	if (prune_by_size)
+	{
+		if (prev_warn_emit == 0 ||
+			GetCurrentStatementStartTimestamp() - prev_warn_emit > 5000000)
+		{
+			ErrorContextCallback *oldcb;
+
+			/* cancel error context callbacks  */
+			oldcb = error_context_stack;
+			error_context_stack = NULL;
+
+			ereport(LOG, (
+						errmsg ("cannot reduce cache size to %d kilobytes, reduced to %d kilobytes",
+								catalog_cache_max_size,	(int)(CacheHdr->ch_global_size / 1024)),
+						errdetail ("Consider increasing the configuration parameter \"catalog_cache_max_size\"."),
+						errhidecontext(true),
+						errhidestmt(true)));
+
+			error_context_stack = oldcb;
+
+			prev_warn_emit = GetCurrentStatementStartTimestamp();
+		}
+	}
+	return nremoved > 0;
+}
+
 /*
  * Enlarge a catcache, doubling the number of buckets.
  */
@@ -858,6 +1061,7 @@ RehashCatCache(CatCache *cp)
 	dlist_head *newbucket;
 	int			newnbuckets;
 	int			i;
+	Size		base_size = MemoryContextGetUsedspace(CacheMemoryContext);
 
 	elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
 		 cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -866,6 +1070,11 @@ RehashCatCache(CatCache *cp)
 	newnbuckets = cp->cc_nbuckets * 2;
 	newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
 
+	/* recalculate memory usage from the first */
+	CacheHdr->ch_global_size -= cp->cc_memusage;
+	cp->cc_memusage = cp->cc_head_alloc_size +
+		MemoryContextGetUsedspace(CacheMemoryContext) - base_size;
+
 	/* Move all entries from old hash table to new. */
 	for (i = 0; i < cp->cc_nbuckets; i++)
 	{
@@ -878,9 +1087,12 @@ RehashCatCache(CatCache *cp)
 
 			dlist_delete(iter.cur);
 			dlist_push_head(&newbucket[hashIndex], &ct->cache_elem);
+			cp->cc_memusage += ct->size;
 		}
 	}
 
+	CacheHdr->ch_global_size += cp->cc_memusage;
+
 	/* Switch to the new array. */
 	pfree(cp->cc_bucket);
 	cp->cc_nbuckets = newnbuckets;
@@ -1260,6 +1472,21 @@ SearchCatCacheInternal(CatCache *cache,
 		 */
 		dlist_move_head(bucket, &ct->cache_elem);
 
+		/* Update access information for pruning */
+		if (ct->naccess < 2)
+			ct->naccess++;
+
+		/*
+		 * We don't want too frequent update of
+		 * LRU. catalog_cache_prune_min_age can be changed on-session so we
+		 * need to maintain the LRU regardless of catalog_cache_prune_min_age.
+		 */
+		if (catcacheclock - ct->lastaccess > MIN_LRU_UPDATE_INTERVAL)
+		{
+			ct->lastaccess = catcacheclock;
+			dlist_move_tail(&CacheHdr->ch_lru_list, &ct->lru_node);
+		}
+
 		/*
 		 * If it's a positive entry, bump its refcount and return it. If it's
 		 * negative, we can report failure to the caller.
@@ -1695,6 +1922,11 @@ SearchCatCacheList(CatCache *cache,
 		/* Now we can build the CatCList entry. */
 		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 		nmembers = list_length(ctlist);
+
+		/*
+		 * Don't waste a time by counting the list in catcache memory usage,
+		 * since it doesn't live a long life.
+		 */
 		cl = (CatCList *)
 			palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *));
 
@@ -1805,11 +2037,13 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 	CatCTup    *ct;
 	HeapTuple	dtp;
 	MemoryContext oldcxt;
+	uint64		base_size = MemoryContextGetUsedspace(CacheMemoryContext);
 
 	/* negative entries have no tuple associated */
 	if (ntp)
 	{
 		int			i;
+		int			tupsize;
 
 		Assert(!negative);
 
@@ -1828,8 +2062,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 		/* Allocate memory for CatCTup and the cached tuple in one go */
 		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
-		ct = (CatCTup *) palloc(sizeof(CatCTup) +
-								MAXIMUM_ALIGNOF + dtp->t_len);
+		tupsize = sizeof(CatCTup) +	MAXIMUM_ALIGNOF + dtp->t_len;
+		ct = (CatCTup *) palloc(tupsize);
 		ct->tuple.t_len = dtp->t_len;
 		ct->tuple.t_self = dtp->t_self;
 		ct->tuple.t_tableOid = dtp->t_tableOid;
@@ -1863,7 +2097,6 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 		Assert(negative);
 		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 		ct = (CatCTup *) palloc(sizeof(CatCTup));
-
 		/*
 		 * Store keys - they'll point into separately allocated memory if not
 		 * by-value.
@@ -1884,18 +2117,37 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 	ct->dead = false;
 	ct->negative = negative;
 	ct->hash_value = hashValue;
+	ct->naccess = 0;
+	ct->lastaccess = catcacheclock;
+	dlist_push_tail(&CacheHdr->ch_lru_list, &ct->lru_node);
 
 	dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
 
 	cache->cc_ntup++;
 	CacheHdr->ch_ntup++;
 
+	ct->size = MemoryContextGetUsedspace(CacheMemoryContext) - base_size;
+	cache->cc_memusage += ct->size;
+	CacheHdr->ch_global_size += ct->size;
+
+	/* increase refcount so that this survives pruning */
+	ct->refcount++;
+
 	/*
-	 * If the hash table has become too full, enlarge the buckets array. Quite
-	 * arbitrarily, we enlarge when fill factor > 2.
+	 * If the hash table has become too full, try cleanup by removing
+	 * infrequently used entries to make a room for the new entry. If it
+	 * failed, enlarge the bucket array instead.  Quite arbitrarily, we try
+	 * this when fill factor > 2.
 	 */
-	if (cache->cc_ntup > cache->cc_nbuckets * 2)
+	if (cache->cc_ntup > cache->cc_nbuckets * 2 &&
+		!CatCacheCleanupOldEntries(cache))
 		RehashCatCache(cache);
+	/* we may still want to prune by entry number, check it */
+	else if (catalog_cache_max_size > 0 &&
+			 CacheHdr->ch_global_size > (Size) catalog_cache_max_size * 1024)
+		CatCacheCleanupOldEntries(cache);
+
+	ct->refcount--;
 
 	return ct;
 }
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index fd51934..0e8b972 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -32,6 +32,7 @@ volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
+volatile sig_atomic_t CatcacheClockTimeoutPending = false;
 volatile sig_atomic_t ConfigReloadPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 156d147..a43132b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -81,6 +81,7 @@
 #include "tsearch/ts_cache.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
+#include "utils/catcache.h"
 #include "utils/guc_tables.h"
 #include "utils/float.h"
 #include "utils/memutils.h"
@@ -2205,6 +2206,39 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Sets the minimum unused duration of cache entries before removal."),
+			gettext_noop("Catalog cache entries that live unused for longer than this seconds are considered to be removed."),
+			GUC_UNIT_S
+		},
+		&catalog_cache_prune_min_age,
+		300, -1, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"catalog_cache_memory_target", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Sets the minimum syscache size to keep."),
+			gettext_noop("Time-based cache pruning starts working after exceeding this size."),
+			GUC_UNIT_KB
+		},
+		&catalog_cache_memory_target,
+		0, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"catalog_cache_max_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Sets the maximum size of catalog cache in kilobytes."),
+			NULL,
+			GUC_UNIT_KB
+		},
+		&catalog_cache_max_size,
+		0, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
 	/*
 	 * We use the hopefully-safely-small value of 100kB as the compiled-in
 	 * default for max_stack_depth.  InitializeGUCOptions will increase it if
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 194f312..2f3f98d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -128,6 +128,9 @@
 #work_mem = 4MB				# min 64kB
 #maintenance_work_mem = 64MB		# min 1MB
 #autovacuum_work_mem = -1		# min 1MB, or -1 to use maintenance_work_mem
+#catalog_cache_memory_target = 0kB	# in kB
+#catalog_cache_prune_min_age = 300s	# -1 disables pruning
+#catalog_cache_max_size = 0kB  		# in kB
 #max_stack_depth = 2MB			# min 100kB
 #shared_memory_type = mmap		# the default is the first option
 					# supported by the operating system:
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index c9e3500..33b800e 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -82,6 +82,7 @@ extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
+extern PGDLLIMPORT volatile sig_atomic_t CatcacheClockTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending;
 
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index 34babde..c1c0977 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -84,10 +84,10 @@ typedef struct MemoryContextData
 	MemoryContext firstchild;	/* head of linked list of children */
 	MemoryContext prevchild;	/* previous child of same parent */
 	MemoryContext nextchild;	/* next child of same parent */
+	Size	usedspace;			/* accumulates consumed memory size */
 	const char *name;			/* context name (just for debugging) */
 	const char *ident;			/* context ID if any (just for debugging) */
 	MemoryContextCallback *reset_cbs;	/* list of reset/delete callbacks */
-	Size	usedspace;	/* accumulates consumed memory size */
 } MemoryContextData;
 
 /* utils/palloc.h contains typedef struct MemoryContextData *MemoryContext */
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index 65d816a..9cfd8b1 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
 
 #include "access/htup.h"
 #include "access/skey.h"
+#include "datatype/timestamp.h"
 #include "lib/ilist.h"
 #include "utils/relcache.h"
 
@@ -61,6 +62,10 @@ typedef struct catcache
 	slist_node	cc_next;		/* list link */
 	ScanKeyData cc_skey[CATCACHE_MAXKEYS];	/* precomputed key info for heap
 											 * scans */
+	int			cc_head_alloc_size;/* consumed memory to allocate this struct */
+	int			cc_memusage;	/* memory usage of this catcache (excluding
+								 * header part) */
+	int			cc_nfreeent;	/* # of entries currently not referenced */
 
 	/*
 	 * Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,7 +124,10 @@ typedef struct catctup
 	bool		dead;			/* dead but not yet removed? */
 	bool		negative;		/* negative cache entry? */
 	HeapTupleData tuple;		/* tuple management header */
-
+	int			naccess;		/* # of access to this entry, up to 2  */
+	TimestampTz	lastaccess;		/* approx. timestamp of the last usage */
+	dlist_node	lru_node;		/* LRU node */
+	int			size;			/* palloc'ed size off this tuple */
 	/*
 	 * The tuple may also be a member of at most one CatCList.  (If a single
 	 * catcache is list-searched with varying numbers of keys, we may have to
@@ -183,12 +191,34 @@ typedef struct catcacheheader
 {
 	slist_head	ch_caches;		/* head of list of CatCache structs */
 	int			ch_ntup;		/* # of tuples in all caches */
+	dlist_head	ch_lru_list;	/* LRU list of all caches */
+	Size		ch_global_size;	/* sum of all caches */
 } CatCacheHeader;
 
 
 /* this extern duplicates utils/memutils.h... */
 extern PGDLLIMPORT MemoryContext CacheMemoryContext;
 
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+extern int catalog_cache_memory_target;
+extern int catalog_cache_max_size;
+
+/* to use as access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+
+/*
+ * SetCatCacheClock - set timestamp for catcache access record and start
+ * maintenance timer if needed. .
+ */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+	catcacheclock = ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
 extern void CreateCacheMemoryContext(void);
 
 extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
