Re: shared-memory based stats collector - Mailing list pgsql-hackers

From Kyotaro Horiguchi
Subject Re: shared-memory based stats collector
Date
Msg-id 20200909.170109.1543582552448760413.horikyota.ntt@gmail.com
Whole thread Raw
In response to Re: shared-memory based stats collector  (Kyotaro Horiguchi <horikyota.ntt@gmail.com>)
List pgsql-hackers
At Tue, 08 Sep 2020 17:55:57 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in 
> At Tue, 08 Sep 2020 17:01:47 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in 
> > At Thu, 3 Sep 2020 13:16:59 -0400, Stephen Frost <sfrost@snowman.net> wrote in> > I've looked through (some of)
thisthread and through the patches also
 
> > > and hope to provide a review of the bits that should be targetting v14
> > > (unlike the above) soon.
> > 
> > Thanks. Current the patch is found to lead to write contention heavier
> > than the current stats collector when nearly a thousand backend
> > heavily write to the same table. I need to address that.
> > 
> > - Collect stats via sockets (in the same way as the current implement)
> >   and write/read to/from shared memory.
> > 
> > - Use our own lock-free (maybe) ring-buffer before stats-write enters
> >   lock-waiting mode, then new stats collector(!) process consumes the
> >   queue.
> > 
> > - Some other measures..

- Make dshash search less frequent. To find the actual source of the
  contension, We're going to measure performance with the attached on
  top of the latest patch let sessions cache the result of dshash
  searches for the session lifetime. (table-dropping vacuum clears the
  local hash.)

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 0ce7b7a891326a3fdcf85b001586f721bef569f9 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyoga.ntt@gmail.com>
Date: Wed, 9 Sep 2020 16:44:57 +0900
Subject: [PATCH v36 8/8] Experimental local cache for dshash.

This patch suffers LWLock contension than unpatched while very many
sessions commits frequently. As an effort to identify the source, this
patch caches dshash information in a local hash to avoid dshash locks.
---
 src/backend/postmaster/pgstat.c | 67 +++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b07add0a4f..caa067e9fe 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -35,6 +35,7 @@
 #include "access/xact.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_proc.h"
+#include "common/hashfn.h"
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -112,6 +113,8 @@ typedef struct StatsShmemStruct
     dsa_pointer slru_stats;        /* Ditto for SLRU stats */
     int            refcount;        /* # of processes that is attaching the shared
                                  * stats memory */
+    pg_atomic_uint64 del_ent_count; /* # of entries deleted. not protected by
+                                       StatsLock */
 }            StatsShmemStruct;
 
 /* BgWriter global statistics counters */
@@ -254,6 +257,7 @@ typedef struct PgStatSharedSLRUStats
 typedef struct PgStatLocalHashEntry
 {
     PgStatHashEntryKey key;        /* hash key */
+    char               status;    /* for simplehash use */
     PgStatEnvelope *env;        /* pointer to stats envelope in heap */
 }            PgStatLocalHashEntry;
 
@@ -280,6 +284,18 @@ static const dshash_parameters dsh_rootparams = {
     LWTRANCHE_STATS
 };
 
+/* define hashtable for dshash caching */
+#define SH_PREFIX pgstat_cache
+#define SH_ELEMENT_TYPE PgStatLocalHashEntry
+#define SH_KEY_TYPE PgStatHashEntryKey
+#define SH_KEY key
+#define SH_HASH_KEY(tb, key) hash_bytes((unsigned char *)&key, sizeof(PgStatHashEntryKey))
+#define SH_EQUAL(tb, a, b) (memcmp(&a, &b, sizeof(PgStatHashEntryKey)) == 0)
+#define SH_SCOPE static inline
+#define SH_DEFINE
+#define SH_DECLARE
+#include "lib/simplehash.h"
+
 /* The shared hash to index activity stats entries. */
 static dshash_table *pgStatSharedHash = NULL;
 
@@ -326,6 +342,8 @@ typedef struct TwoPhasePgStatRecord
 } TwoPhasePgStatRecord;
 
 /* Variables for backend status snapshot */
+static pgstat_cache_hash *pgStatEntHash = NULL;
+static int     pgStatEntHashAge = 0;
 static MemoryContext pgStatLocalContext = NULL;
 static MemoryContext pgStatSnapshotContext = NULL;
 static HTAB *pgStatSnapshotHash = NULL;
@@ -437,6 +455,7 @@ StatsShmemInit(void)
         Assert(!found);
 
         StatsShmem->stats_dsa_handle = DSM_HANDLE_INVALID;
+        pg_atomic_init_u64(&StatsShmem->del_ent_count, 0);
     }
 }
 
@@ -1432,6 +1451,9 @@ pgstat_vacuum_stat(void)
 
         delete_stat_entry(p->type, p->databaseid, p->objectid, true);
     }
+
+    if (nvictims > 0)
+        pg_atomic_add_fetch_u64(&StatsShmem->del_ent_count, 1);
 }
 
 
@@ -1493,6 +1515,8 @@ pgstat_drop_database(Oid databaseid)
         delete_stat_entry((*p)->type, (*p)->databaseid, (*p)->objectid, true);
 
     pfree(envlist);
+
+    pg_atomic_add_fetch_u64(&StatsShmem->del_ent_count, 1);
 }
 
 
@@ -5119,6 +5143,7 @@ get_stat_entry(PgStatTypes type, Oid dbid, Oid objid,
 {
     bool        create = (initfunc != NULL);
     PgStatHashEntry *shent;
+    PgStatLocalHashEntry *loent;
     PgStatEnvelope *shenv = NULL;
     PgStatHashEntryKey key;
     bool        myfound;
@@ -5128,6 +5153,31 @@ get_stat_entry(PgStatTypes type, Oid dbid, Oid objid,
     key.type = type;
     key.databaseid = dbid;
     key.objectid = objid;
+
+    if (pgStatEntHash)
+    {
+        uint64 currage;
+
+        currage = pg_atomic_read_u64(&StatsShmem->del_ent_count);
+
+        if (currage == pgStatEntHashAge)
+        {
+            loent = pgstat_cache_lookup(pgStatEntHash, key);
+
+            if (loent)
+            {
+                if (found)
+                    *found = true;
+                return loent->env;
+            }
+        }
+        else
+        {
+            pgstat_cache_destroy(pgStatEntHash);
+            pgStatEntHash = NULL;
+        }
+    }
+
     shent = dshash_find_extended(pgStatSharedHash, &key,
                                  create, nowait, create, &myfound);
     if (shent)
@@ -5159,6 +5209,23 @@ get_stat_entry(PgStatTypes type, Oid dbid, Oid objid,
             shenv = dsa_get_address(area, shent->env);
 
         dshash_release_lock(pgStatSharedHash, shent);
+
+        /* register to local hash if possible */
+        if (pgStatEntHash || CacheMemoryContext)
+        {
+            if (pgStatEntHash == NULL)
+            {
+                pgStatEntHash =
+                    pgstat_cache_create(CacheMemoryContext,
+                                        PGSTAT_TABLE_HASH_SIZE, NULL);
+                pgStatEntHashAge =
+                    pg_atomic_read_u64(&StatsShmem->del_ent_count);
+            }
+    
+            loent = pgstat_cache_insert(pgStatEntHash, key, &myfound);
+            Assert(!myfound);
+            loent->env = shenv;
+        }
     }
 
     if (found)
-- 
2.18.4


pgsql-hackers by date:

Previous
From: gkokolatos@pm.me
Date:
Subject: Re: PATCH: Attempt to make dbsize a bit more consistent
Next
From: Ian Barwick
Date:
Subject: Re: Improving connection scalability: GetSnapshotData()