Re: Protect syscache from bloating with negative cache entries - Mailing list pgsql-hackers

From Kyotaro Horiguchi
Subject Re: Protect syscache from bloating with negative cache entries
Date
Msg-id 20201105.162655.1850883840029889601.horikyota.ntt@gmail.com
Whole thread Raw
In response to Re: Protect syscache from bloating with negative cache entries  (Kyotaro Horiguchi <horikyota.ntt@gmail.com>)
List pgsql-hackers
Hello.

The attached is the version that is compactified from the previous
version.

At Thu, 01 Oct 2020 16:47:18 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in 
> This is the rebased version.

It occurred to me suddenly that static parameters to inline functions
causes optimization.  I split SearchCatCacheInternal() into two
almost-the-same functions SearchCatCacheInternalb() and -e() until the
previous version but in turn I merged the two functions into the
original function and added a parameter "do_expire".

Then compared at machine-code level of the two deduced functions
SearchCatCache1b and SearchCatCache1e and confirmed that the
expiration-related code is eliminated from the former.

The lines prefixed by '+' are the instructions corresponding the
following C-code, which are eliminated in SearchCatCache1b.

0000000000002770 <SearchCatCache1e>:
    2770:         push   %r15
    2772:         push   %r14
...
    2849:         mov    %rbp,(%rbx)
    284c:         mov    %rbx,(%rax)
    284f:         mov    %rbx,0x8(%rbp)
+   2853:         mov    0x30(%rbx),%eax  # %eax = ct->naccess
+   2856:         mov    $0x2,%edx
+   285b:         add    $0x1,%eax  #  ct->access++
+   285e:         cmove  %edx,%eax  #  if(ct->access == 0) %eax = 2
    2861:         xor    %ebp,%ebp
    2863:         cmpb   $0x0,0x15(%rbx)  # (if (!ct->negative))
+   2867:         mov    %eax,0x30(%rbx)  # ct->access = %eax
+   286a:         mov    0x0(%rip),%rax   # %rax = catcacheclock
+   2871:         mov    %rax,0x38(%rbx)  # ct->lastaccess = %rax
    2875:         jne    289a <SearchCatCache1e+0x12a>
    2877:         mov    0x0(%rip),%rdi
    

>    if (do_expire)
>    {
>        ct->naccess++;
>        if (unlikely(ct->naccess == 0))
>            ct->naccess = 2;
>        ct->lastaccess = catcacheclock;
>    }

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 5ac54752a8c4f555b3ccc405c65e74614057bf34 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Fri, 10 Jan 2020 15:02:26 +0900
Subject: [PATCH v3 1/3] base_change

Adds struct members needed by catcache expiration feature and a GUC
variable that controls the behavior of the feature. But no substantial
code is not added yet.

If existence of some variables alone can cause degradation,
benchmarking after this patch shows that.
---
 src/backend/access/transam/xact.c  |  3 +++
 src/backend/utils/cache/catcache.c | 15 +++++++++++++++
 src/backend/utils/misc/guc.c       | 13 +++++++++++++
 src/include/utils/catcache.h       | 20 ++++++++++++++++++++
 4 files changed, 51 insertions(+)

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index af6afcebb1..1040713955 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1085,6 +1085,9 @@ ForceSyncCommit(void)
 static void
 AtStart_Cache(void)
 {
+    if (xactStartTimestamp != 0)
+        SetCatCacheClock(xactStartTimestamp);
+
     AcceptInvalidationMessages();
 }
 
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3613ae5f44..c5bad63aac 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -60,9 +60,18 @@
 #define CACHE_elog(...)
 #endif
 
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = 300;
+
 /* Cache management header --- pointer is NULL until created */
 static CatCacheHeader *CacheHdr = NULL;
 
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz    catcacheclock = 0;
+
 static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
                                                int nkeys,
                                                Datum v1, Datum v2,
@@ -99,6 +108,12 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
 static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
                              Datum *srckeys, Datum *dstkeys);
 
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+    catalog_cache_prune_min_age = newval;
+}
 
 /*
  *                    internal support functions
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a62d64eaa4..628a95376c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,8 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
+#include "utils/catcache.h"
+#include "utils/guc_tables.h"
 #include "utils/float.h"
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
@@ -2358,6 +2360,17 @@ static struct config_int ConfigureNamesInt[] =
         NULL, NULL, NULL
     },
 
+    {
+        {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+            gettext_noop("System catalog cache entries that live unused for longer than this seconds are considered
forremoval."),
 
+            gettext_noop("The value of -1 turns off pruning."),
+            GUC_UNIT_S
+        },
+        &catalog_cache_prune_min_age,
+        300, -1, INT_MAX,
+        NULL, assign_catalog_cache_prune_min_age, 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/include/utils/catcache.h b/src/include/utils/catcache.h
index f4aa316604..3d3870f05a 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,7 @@ typedef struct catcache
     slist_node    cc_next;        /* list link */
     ScanKeyData cc_skey[CATCACHE_MAXKEYS];    /* precomputed key info for heap
                                              * scans */
+    TimestampTz    cc_oldest_ts;    /* timestamp of the oldest tuple in the hash */
 
     /*
      * Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,8 @@ typedef struct catctup
     bool        dead;            /* dead but not yet removed? */
     bool        negative;        /* negative cache entry? */
     HeapTupleData tuple;        /* tuple management header */
+    unsigned int naccess;        /* # of access to this entry */
+    TimestampTz    lastaccess;        /* timestamp of the last usage */
 
     /*
      * The tuple may also be a member of at most one CatCList.  (If a single
@@ -189,6 +193,22 @@ typedef struct catcacheheader
 /* this extern duplicates utils/memutils.h... */
 extern PGDLLIMPORT MemoryContext CacheMemoryContext;
 
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clodk */
+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,
-- 
2.18.4

From 6d049e7a7a80589abaa9efa96599a518d254a72b Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Thu, 9 Jan 2020 19:22:18 +0900
Subject: [PATCH v3 2/3] Make CatCacheSearchN indirect functions

Some experiments showed that we could add new features avoidng
degradtion by making SearchCatCacheN functions replaceable using
indirect calling.
---
 src/backend/utils/cache/catcache.c | 58 ++++++++++++++++++++----------
 src/include/utils/catcache.h       | 40 ++++++++++++++++++---
 2 files changed, 75 insertions(+), 23 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index c5bad63aac..7ac59bcdd6 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -84,6 +84,15 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
                                                 Datum v1, Datum v2,
                                                 Datum v3, Datum v4);
 
+static HeapTuple SearchCatCacheb(CatCache *cache,
+                                 Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1b(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2b(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3b(CatCache *cache,
+                                  Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4b(CatCache *cache,
+                                  Datum v1, Datum v2, Datum v3, Datum v4);
+
 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
                                            Datum v1, Datum v2, Datum v3, Datum v4);
 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -108,6 +117,16 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
 static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
                              Datum *srckeys, Datum *dstkeys);
 
+static SearchCatCacheFuncsType catcache_base = {
+    SearchCatCacheb,
+    SearchCatCache1b,
+    SearchCatCache2b,
+    SearchCatCache3b,
+    SearchCatCache4b
+};
+
+SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
+
 /* GUC assign function */
 void
 assign_catalog_cache_prune_min_age(int newval, void *extra)
@@ -817,6 +836,9 @@ InitCatCache(int id,
         CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));
         slist_init(&CacheHdr->ch_caches);
         CacheHdr->ch_ntup = 0;
+
+        SearchCatCacheFuncs = &catcache_base;
+
 #ifdef CATCACHE_STATS
         /* set up to dump stats at backend exit */
         on_proc_exit(CatCachePrintStats, 0);
@@ -1158,12 +1180,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
  * the caller need not go to the trouble of converting it to a fully
  * null-padded NAME.
  */
-HeapTuple
-SearchCatCache(CatCache *cache,
-               Datum v1,
-               Datum v2,
-               Datum v3,
-               Datum v4)
+static HeapTuple
+SearchCatCacheb(CatCache *cache,
+                Datum v1,
+                Datum v2,
+                Datum v3,
+                Datum v4)
 {
     return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
 }
@@ -1175,33 +1197,33 @@ SearchCatCache(CatCache *cache,
  * bit faster than SearchCatCache().
  */
 
-HeapTuple
-SearchCatCache1(CatCache *cache,
-                Datum v1)
+static HeapTuple
+SearchCatCache1b(CatCache *cache,
+                 Datum v1)
 {
     return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
 }
 
 
-HeapTuple
-SearchCatCache2(CatCache *cache,
-                Datum v1, Datum v2)
+static HeapTuple
+SearchCatCache2b(CatCache *cache,
+                 Datum v1, Datum v2)
 {
     return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
 }
 
 
-HeapTuple
-SearchCatCache3(CatCache *cache,
-                Datum v1, Datum v2, Datum v3)
+static HeapTuple
+SearchCatCache3b(CatCache *cache,
+                 Datum v1, Datum v2, Datum v3)
 {
     return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
 }
 
 
-HeapTuple
-SearchCatCache4(CatCache *cache,
-                Datum v1, Datum v2, Datum v3, Datum v4)
+static HeapTuple
+SearchCatCache4b(CatCache *cache,
+                 Datum v1, Datum v2, Datum v3, Datum v4)
 {
     return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
 }
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index 3d3870f05a..f9e9889339 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -189,6 +189,36 @@ typedef struct catcacheheader
     int            ch_ntup;        /* # of tuples in all caches */
 } CatCacheHeader;
 
+typedef HeapTuple (*SearchCatCache_fn)(CatCache *cache,
+                                       Datum v1, Datum v2, Datum v3, Datum v4);
+typedef HeapTuple (*SearchCatCache1_fn)(CatCache *cache, Datum v1);
+typedef HeapTuple (*SearchCatCache2_fn)(CatCache *cache, Datum v1, Datum v2);
+typedef HeapTuple (*SearchCatCache3_fn)(CatCache *cache, Datum v1, Datum v2,
+                                        Datum v3);
+typedef HeapTuple (*SearchCatCache4_fn)(CatCache *cache,
+                                        Datum v1, Datum v2, Datum v3, Datum v4);
+
+typedef struct SearchCatCacheFuncsType
+{
+    SearchCatCache_fn    SearchCatCache;
+    SearchCatCache1_fn    SearchCatCache1;
+    SearchCatCache2_fn    SearchCatCache2;
+    SearchCatCache3_fn    SearchCatCache3;
+    SearchCatCache4_fn    SearchCatCache4;
+} SearchCatCacheFuncsType;
+
+extern PGDLLIMPORT SearchCatCacheFuncsType *SearchCatCacheFuncs;
+
+#define SearchCatCache(cache, v1, v2, v3, v4) \
+    SearchCatCacheFuncs->SearchCatCache(cache, v1, v2, v3, v4)
+#define SearchCatCache1(cache, v1) \
+    SearchCatCacheFuncs->SearchCatCache1(cache, v1)
+#define SearchCatCache2(cache, v1, v2) \
+    SearchCatCacheFuncs->SearchCatCache2(cache, v1, v2)
+#define SearchCatCache3(cache, v1, v2, v3) \
+    SearchCatCacheFuncs->SearchCatCache3(cache, v1, v2, v3)
+#define SearchCatCache4(cache, v1, v2, v3, v4) \
+    SearchCatCacheFuncs->SearchCatCache4(cache, v1, v2, v3, v4)
 
 /* this extern duplicates utils/memutils.h... */
 extern PGDLLIMPORT MemoryContext CacheMemoryContext;
@@ -216,15 +246,15 @@ extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
                               int nbuckets);
 extern void InitCatCachePhase2(CatCache *cache, bool touch_index);
 
-extern HeapTuple SearchCatCache(CatCache *cache,
+extern HeapTuple (*SearchCatCache)(CatCache *cache,
                                 Datum v1, Datum v2, Datum v3, Datum v4);
-extern HeapTuple SearchCatCache1(CatCache *cache,
+extern HeapTuple (*SearchCatCache1)(CatCache *cache,
                                  Datum v1);
-extern HeapTuple SearchCatCache2(CatCache *cache,
+extern HeapTuple (*SearchCatCache2)(CatCache *cache,
                                  Datum v1, Datum v2);
-extern HeapTuple SearchCatCache3(CatCache *cache,
+extern HeapTuple (*SearchCatCache3)(CatCache *cache,
                                  Datum v1, Datum v2, Datum v3);
-extern HeapTuple SearchCatCache4(CatCache *cache,
+extern HeapTuple (*SearchCatCache4)(CatCache *cache,
                                  Datum v1, Datum v2, Datum v3, Datum v4);
 extern void ReleaseCatCache(HeapTuple tuple);
 
-- 
2.18.4

From 183b4bea37807cd83f2d3688a43f27d86a784c7b Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Fri, 10 Jan 2020 15:08:54 +0900
Subject: [PATCH v3 3/3] CatCache expiration feature.

This adds the catcache expiration feature to the catcache mechanism.

Current catcache doesn't remove an entry and there's a case where many
hash entries occupy large amont of memory , being not accessed ever
after. This can be a quite serious issue on the cases of long-running
sessions.  The expiration feature keeps process memory usage below
certain amount, in exchange of some extent of degradation if it is
turned on.
---
 src/backend/utils/cache/catcache.c | 219 +++++++++++++++++++++++++++--
 1 file changed, 211 insertions(+), 8 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 7ac59bcdd6..a07b434fb6 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
 #include "utils/rel.h"
 #include "utils/resowner_private.h"
 #include "utils/syscache.h"
+#include "utils/timestamp.h"
 
 
  /* #define CACHEDEBUG */    /* turns DEBUG elogs on */
@@ -72,7 +73,8 @@ static CatCacheHeader *CacheHdr = NULL;
 /* Clock for the last accessed time of a catcache entry. */
 TimestampTz    catcacheclock = 0;
 
-static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
+static inline HeapTuple SearchCatCacheInternal(bool do_expire,
+                                               CatCache *cache,
                                                int nkeys,
                                                Datum v1, Datum v2,
                                                Datum v3, Datum v4);
@@ -93,6 +95,17 @@ static HeapTuple SearchCatCache3b(CatCache *cache,
 static HeapTuple SearchCatCache4b(CatCache *cache,
                                   Datum v1, Datum v2, Datum v3, Datum v4);
 
+static HeapTuple SearchCatCachee(CatCache *cache,
+                                 Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1e(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2e(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3e(CatCache *cache,
+                                  Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4e(CatCache *cache,
+                                  Datum v1, Datum v2, Datum v3, Datum v4);
+
+static bool CatCacheCleanupOldEntries(CatCache *cp);
+
 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
                                            Datum v1, Datum v2, Datum v3, Datum v4);
 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -125,13 +138,35 @@ static SearchCatCacheFuncsType catcache_base = {
     SearchCatCache4b
 };
 
+static SearchCatCacheFuncsType catcache_expire = {
+    SearchCatCachee,
+    SearchCatCache1e,
+    SearchCatCache2e,
+    SearchCatCache3e,
+    SearchCatCache4e
+};
+
 SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
 
+/* set catcache function set according to guc variables */
+static void
+set_catcache_functions(void)
+{
+    if (catalog_cache_prune_min_age < 0)
+        SearchCatCacheFuncs = &catcache_base;
+    else
+        SearchCatCacheFuncs = &catcache_expire;
+}
+
+
 /* GUC assign function */
 void
 assign_catalog_cache_prune_min_age(int newval, void *extra)
 {
     catalog_cache_prune_min_age = newval;
+
+    /* choose corresponding function set */
+    set_catcache_functions();
 }
 
 /*
@@ -837,7 +872,7 @@ InitCatCache(int id,
         slist_init(&CacheHdr->ch_caches);
         CacheHdr->ch_ntup = 0;
 
-        SearchCatCacheFuncs = &catcache_base;
+        set_catcache_functions();
 
 #ifdef CATCACHE_STATS
         /* set up to dump stats at backend exit */
@@ -900,6 +935,10 @@ RehashCatCache(CatCache *cp)
     int            newnbuckets;
     int            i;
 
+    /* try removing old entries before expanding hash */
+    if (CatCacheCleanupOldEntries(cp))
+        return;
+
     elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
          cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
 
@@ -1187,7 +1226,7 @@ SearchCatCacheb(CatCache *cache,
                 Datum v3,
                 Datum v4)
 {
-    return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
+    return SearchCatCacheInternal(false, cache, cache->cc_nkeys, v1, v2, v3, v4);
 }
 
 
@@ -1201,7 +1240,7 @@ static HeapTuple
 SearchCatCache1b(CatCache *cache,
                  Datum v1)
 {
-    return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
+    return SearchCatCacheInternal(false, cache, 1, v1, 0, 0, 0);
 }
 
 
@@ -1209,7 +1248,7 @@ static HeapTuple
 SearchCatCache2b(CatCache *cache,
                  Datum v1, Datum v2)
 {
-    return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
+    return SearchCatCacheInternal(false, cache, 2, v1, v2, 0, 0);
 }
 
 
@@ -1217,7 +1256,7 @@ static HeapTuple
 SearchCatCache3b(CatCache *cache,
                  Datum v1, Datum v2, Datum v3)
 {
-    return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
+    return SearchCatCacheInternal(false, cache, 3, v1, v2, v3, 0);
 }
 
 
@@ -1225,14 +1264,18 @@ static HeapTuple
 SearchCatCache4b(CatCache *cache,
                  Datum v1, Datum v2, Datum v3, Datum v4)
 {
-    return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
+    return SearchCatCacheInternal(false, cache, 4, v1, v2, v3, v4);
 }
 
 /*
  * Work-horse for SearchCatCache/SearchCatCacheN.
+ *
+ * This function expects smarter compiler reduces useless paths by calling with
+ * fixed parameters.
  */
 static inline HeapTuple
-SearchCatCacheInternal(CatCache *cache,
+SearchCatCacheInternal(bool do_expire,
+                       CatCache *cache,
                        int nkeys,
                        Datum v1,
                        Datum v2,
@@ -1301,6 +1344,20 @@ SearchCatCacheInternal(CatCache *cache,
          */
         dlist_move_head(bucket, &ct->cache_elem);
 
+        /*
+         * Prolong life of this entry. Since we want run as less instructions
+         * as possible and want the branch be stable for performance reasons,
+         * we don't give a strict cap on the counter. All numbers above 1 will
+         * be regarded as 2 in CatCacheCleanupOldEntries().
+         */
+        if (do_expire)
+        {
+            ct->naccess++;
+            if (unlikely(ct->naccess == 0))
+                ct->naccess = 2;
+            ct->lastaccess = catcacheclock;
+        }
+
         /*
          * If it's a positive entry, bump its refcount and return it. If it's
          * negative, we can report failure to the caller.
@@ -1462,6 +1519,150 @@ SearchCatCacheMiss(CatCache *cache,
     return &ct->tuple;
 }
 
+/*
+ *    SearchCatCache with entry pruning
+ *
+ *  These functions works the same way with SearchCatCacheNb() functions except
+ *  that less-used entries are removed following catalog_cache_prune_min_age
+ *  setting.
+ */
+static HeapTuple
+SearchCatCachee(CatCache *cache,
+               Datum v1,
+               Datum v2,
+               Datum v3,
+               Datum v4)
+{
+    return SearchCatCacheInternal(true, cache, cache->cc_nkeys, v1, v2, v3, v4);
+}
+
+
+/*
+ * SearchCatCacheN() are SearchCatCache() versions for a specific number of
+ * arguments. The compiler can inline the body and unroll loops, making them a
+ * bit faster than SearchCatCache().
+ */
+
+static HeapTuple
+SearchCatCache1e(CatCache *cache,
+                Datum v1)
+{
+    return SearchCatCacheInternal(true, cache, 1, v1, 0, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache2e(CatCache *cache,
+                Datum v1, Datum v2)
+{
+    return SearchCatCacheInternal(true, cache, 2, v1, v2, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache3e(CatCache *cache,
+                Datum v1, Datum v2, Datum v3)
+{
+    return SearchCatCacheInternal(true, cache, 3, v1, v2, v3, 0);
+}
+
+
+static HeapTuple
+SearchCatCache4e(CatCache *cache,
+                Datum v1, Datum v2, Datum v3, Datum v4)
+{
+    return SearchCatCacheInternal(true, cache, 4, v1, v2, v3, v4);
+}
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+    int        nremoved = 0;
+    int        i;
+    long    oldest_ts = catcacheclock;
+    long    age;
+    int        us;
+
+    /* Return immediately if disabled */
+    if (catalog_cache_prune_min_age < 0)
+        return false;
+
+    /* Don't scan the hash when we know we don't have prunable entries */
+    TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+    if (age < catalog_cache_prune_min_age)
+        return false;
+
+    /* Scan over the whole hash to find entries to remove */
+    for (i = 0 ; i < cp->cc_nbuckets ; i++)
+    {
+        dlist_mutable_iter    iter;
+
+        dlist_foreach_modify(iter, &cp->cc_bucket[i])
+        {
+            CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+            /* Don't remove referenced entries */
+            if (ct->refcount == 0 &&
+                (ct->c_list == NULL || ct->c_list->refcount == 0))
+            {
+                /*
+                 * Calculate the duration from the time from the last access
+                 * to the "current" time. catcacheclock is updated
+                 * per-statement basis and additionaly udpated periodically
+                 * during a long running query.
+                 */
+                TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+                if (age > catalog_cache_prune_min_age)
+                {
+                    /*
+                     * Entries that are not accessed after the last pruning
+                     * are removed in that seconds, and their lives are
+                     * prolonged according to how many times they are accessed
+                     * 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 > 2)
+                        ct->naccess = 1;
+                    else if (ct->naccess > 0)
+                        ct->naccess--;
+                    else
+                    {
+                        CatCacheRemoveCTup(cp, ct);
+                        nremoved++;
+
+                        /* don't update oldest_ts by removed entry */
+                        continue;
+                    }
+                }
+            }
+
+            /* update oldest timestamp if the entry remains alive */
+            if (ct->lastaccess < oldest_ts)
+                oldest_ts = ct->lastaccess;
+        }
+    }
+
+    cp->cc_oldest_ts = oldest_ts;
+
+    if (nremoved > 0)
+        elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+             cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+    return nremoved > 0;
+}
+
+
 /*
  *    ReleaseCatCache
  *
@@ -1925,6 +2126,8 @@ 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_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
 
-- 
2.18.4


pgsql-hackers by date:

Previous
From: yuzuko
Date:
Subject: Re: Autovacuum on partitioned table (autoanalyze)
Next
From: Peter Eisentraut
Date:
Subject: Re: Move catalog toast table and index declarations