Subject: [PATCH 2/5] Benchmark extension and required core change

From: Kyotaro Horiguchi <[email-masked]>
Date: Fri, 28 Jun 2019 17:03:07 +0900

Micro benchmark extension for SearchSysCache and required core-side
code.
---
 contrib/catcachebench/Makefile               |  17 ++
 contrib/catcachebench/catcachebench--0.0.sql |   9 +
 contrib/catcachebench/catcachebench.c        | 281 +++++++++++++++++++++++++++
 contrib/catcachebench/catcachebench.control  |   6 +
 src/backend/utils/cache/catcache.c           |  13 ++
 5 files changed, 326 insertions(+)
 create mode 100644 contrib/catcachebench/Makefile
 create mode 100644 contrib/catcachebench/catcachebench--0.0.sql
 create mode 100644 contrib/catcachebench/catcachebench.c
 create mode 100644 contrib/catcachebench/catcachebench.control

diff --git a/contrib/catcachebench/Makefile b/contrib/catcachebench/Makefile
new file mode 100644
index 0000000000..0478818b25
--- /dev/null
+++ b/contrib/catcachebench/Makefile
@@ -0,0 +1,17 @@
+MODULE_big = catcachebench
+OBJS = catcachebench.o
+
+EXTENSION = catcachebench
+DATA = catcachebench--0.0.sql
+PGFILEDESC = "catcachebench - benchmark for catcache pruning feature"
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/catcachebench
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/catcachebench/catcachebench--0.0.sql b/contrib/catcachebench/catcachebench--0.0.sql
new file mode 100644
index 0000000000..e091baaaa7
--- /dev/null
+++ b/contrib/catcachebench/catcachebench--0.0.sql
@@ -0,0 +1,9 @@
+/* contrib/catcachebench/catcachebench--0.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION catcachebench" to load this file. \quit
+
+CREATE FUNCTION catcachebench(IN type int)
+RETURNS double precision
+AS 'MODULE_PATHNAME', 'catcachebench'
+LANGUAGE C STRICT VOLATILE;
diff --git a/contrib/catcachebench/catcachebench.c b/contrib/catcachebench/catcachebench.c
new file mode 100644
index 0000000000..0cebbbde4f
--- /dev/null
+++ b/contrib/catcachebench/catcachebench.c
@@ -0,0 +1,281 @@
+/*
+ * catcachebench: test code for cache pruning feature
+ */
+#include "postgres.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_statistic.h"
+#include "executor/spi.h"
+#include "libpq/pqsignal.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+
+Oid		tableoids[10000];
+int		ntables = 0;
+int16	attnums[1000];
+int		natts = 0;
+
+PG_MODULE_MAGIC;
+
+double catcachebench1(void);
+double catcachebench2(void);
+double catcachebench3(void);
+void collectinfo(void);
+void catcachewarmup(void);
+
+PG_FUNCTION_INFO_V1(catcachebench);
+
+Datum
+catcachebench(PG_FUNCTION_ARGS)
+{
+	int		testtype = PG_GETARG_INT32(0);
+	double	ms;
+	extern bool _catcache_shrink_buckets;
+
+	collectinfo();
+
+	/* flush the catalog -- safe? don't mind. */
+	
+	_catcache_shrink_buckets = true;
+	CatalogCacheFlushCatalog(StatisticRelationId);
+	_catcache_shrink_buckets = false;
+
+	switch (testtype)
+	{
+	case 0:
+		catcachewarmup(); /* prewarm of syscatalog */
+		PG_RETURN_NULL();
+	case 1:
+		ms = catcachebench1(); break;
+	case 2:
+		ms = catcachebench2(); break;
+	case 3:
+		ms = catcachebench3(); break;
+	default:
+		elog(ERROR, "Invalid test type: %d", testtype);
+	}
+
+	PG_RETURN_DATUM(Float8GetDatum(ms));
+}
+
+/*
+ * fetch all attribute entires of all tables.
+ */
+double
+catcachebench1(void)
+{
+	int t, a;
+	instr_time	start,
+				duration;
+
+	PG_SETMASK(&BlockSig);
+	INSTR_TIME_SET_CURRENT(start);
+	for (t = 0 ; t < ntables ; t++)
+	{
+		for (a = 0 ; a < natts ; a++)
+		{
+			HeapTuple tup;
+
+			tup = SearchSysCache3(STATRELATTINH,
+								  ObjectIdGetDatum(tableoids[t]),
+								  Int16GetDatum(attnums[a]),
+								  BoolGetDatum(false));
+			/* should be null, but.. */
+			if (HeapTupleIsValid(tup))
+				ReleaseSysCache(tup);
+		}
+	}
+	INSTR_TIME_SET_CURRENT(duration);
+	INSTR_TIME_SUBTRACT(duration, start);
+	PG_SETMASK(&UnBlockSig);
+
+	return INSTR_TIME_GET_MILLISEC(duration);
+};
+
+/*
+ * fetch all attribute entires of a table 6000 times.
+ */
+double
+catcachebench2(void)
+{
+	const int clock_step = 100;
+	int t, a;
+	instr_time	start,
+				duration;
+
+	PG_SETMASK(&BlockSig);
+	INSTR_TIME_SET_CURRENT(start);
+	for (t = 0 ; t < 60000 ; t++)
+	{
+		int ct = clock_step;
+
+		/*
+		 * catcacheclock is updated by transaction timestamp, so needs to
+		 * be updated by other means for this test to work. Here I choosed
+		 * to update the clock every 100 times of table scans.
+		 */
+		if (--ct < 0)
+		{
+			// We don't have it yet.
+			//SetCatCacheClock(GetCurrentTimestamp());
+			GetCurrentTimestamp();
+			ct = clock_step;
+		}
+		for (a = 0 ; a < natts ; a++)
+		{
+			HeapTuple tup;
+
+			tup = SearchSysCache3(STATRELATTINH,
+								  ObjectIdGetDatum(tableoids[0]),
+								  Int16GetDatum(attnums[a]),
+								  BoolGetDatum(false));
+			/* should be null, but.. */
+			if (HeapTupleIsValid(tup))
+				ReleaseSysCache(tup);
+		}
+	}
+	INSTR_TIME_SET_CURRENT(duration);
+	INSTR_TIME_SUBTRACT(duration, start);
+	PG_SETMASK(&UnBlockSig);
+
+	return INSTR_TIME_GET_MILLISEC(duration);
+};
+
+/*
+ * fetch all attribute entires of all tables twice with having expiration
+ * happen.
+ */
+double
+catcachebench3(void)
+{
+	const int clock_step = 100;
+	int i, t, a;
+	instr_time	start,
+				duration;
+
+	PG_SETMASK(&BlockSig);
+	INSTR_TIME_SET_CURRENT(start);
+	for (i = 0 ; i < 2 ; i++)
+	{
+		int ct = clock_step;
+
+		for (t = 0 ; t < ntables ; t++)
+		{
+			/*
+			 * catcacheclock is updated by transaction timestamp, so needs to
+			 * be updated by other means for this test to work. Here I choosed
+			 * to update the clock every 100 tables scan.
+			 */
+			if (--ct < 0)
+			{
+				// We don't have it yet.
+				//SetCatCacheClock(GetCurrentTimestamp());
+				GetCurrentTimestamp();
+				ct = clock_step;
+			}
+			for (a = 0 ; a < natts ; a++)
+			{
+				HeapTuple tup;
+
+				tup = SearchSysCache3(STATRELATTINH,
+									  ObjectIdGetDatum(tableoids[t]),
+									  Int16GetDatum(attnums[a]),
+									  BoolGetDatum(false));
+				/* should be null, but.. */
+				if (HeapTupleIsValid(tup))
+					ReleaseSysCache(tup);
+			}
+		}
+	}
+	INSTR_TIME_SET_CURRENT(duration);
+	INSTR_TIME_SUBTRACT(duration, start);
+	PG_SETMASK(&UnBlockSig);
+
+	return INSTR_TIME_GET_MILLISEC(duration);
+};
+
+void
+catcachewarmup(void)
+{
+	int t, a;
+
+	/* load up catalog tables */
+	for (t = 0 ; t < ntables ; t++)
+	{
+		for (a = 0 ; a < natts ; a++)
+		{
+			HeapTuple tup;
+
+			tup = SearchSysCache3(STATRELATTINH,
+								  ObjectIdGetDatum(tableoids[t]),
+								  Int16GetDatum(attnums[a]),
+								  BoolGetDatum(false));
+			/* should be null, but.. */
+			if (HeapTupleIsValid(tup))
+				ReleaseSysCache(tup);
+		}
+	}
+}
+
+void
+collectinfo(void)
+{
+	int ret;
+	Datum	values[10000];
+	bool	nulls[10000];
+	Oid		types0[] = {OIDOID};
+	int i;
+
+	ntables = 0;
+	natts = 0;
+
+	SPI_connect();
+	/* collect target tables */
+	ret = SPI_execute("select oid from pg_class where relnamespace = (select oid from pg_namespace where nspname = \'test\')",
+					  true, 0);
+	if (ret != SPI_OK_SELECT)
+		elog(ERROR, "Failed 1");
+	if (SPI_processed == 0)
+		elog(ERROR, "no relation found in schema \"test\"");
+	if (SPI_processed > 10000)
+		elog(ERROR, "too many relation found in schema \"test\"");
+
+	for (i = 0 ; i < SPI_processed ; i++)
+	{
+		heap_deform_tuple(SPI_tuptable->vals[i], SPI_tuptable->tupdesc,
+						  values, nulls);
+		if (nulls[0])
+			elog(ERROR, "Failed 2");
+
+		tableoids[ntables++] = DatumGetObjectId(values[0]);
+	}
+	SPI_finish();
+	elog(DEBUG1, "%d tables found", ntables);
+
+	values[0] = ObjectIdGetDatum(tableoids[0]);
+	nulls[0] = false;
+	SPI_connect();
+	ret = SPI_execute_with_args("select attnum from pg_attribute where attrelid = (select oid from pg_class where oid = $1)",
+								1, types0, values, NULL, true, 0);
+	if (SPI_processed == 0)
+		elog(ERROR, "no attribute found in table %d", tableoids[0]);
+	if (SPI_processed > 10000)
+		elog(ERROR, "too many relation found in table %d", tableoids[0]);
+	
+	/* collect target attributes. assuming all tables have the same attnums */
+	for (i = 0 ; i < SPI_processed ; i++)
+	{
+		int16 attnum;
+
+		heap_deform_tuple(SPI_tuptable->vals[i], SPI_tuptable->tupdesc,
+						  values, nulls);
+		if (nulls[0])
+			elog(ERROR, "Failed 3");
+		attnum = DatumGetInt16(values[0]);
+
+		if (attnum > 0)
+			attnums[natts++] = attnum;
+	}
+	SPI_finish();
+	elog(DEBUG1, "%d attributes found", natts);
+}
diff --git a/contrib/catcachebench/catcachebench.control b/contrib/catcachebench/catcachebench.control
new file mode 100644
index 0000000000..3fc9d2e420
--- /dev/null
+++ b/contrib/catcachebench/catcachebench.control
@@ -0,0 +1,6 @@
+# catcachebench
+
+comment = 'benchmark for catcache pruning'
+default_version = '0.0'
+module_pathname = '$libdir/catcachebench'
+relocatable = true
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 8fc067ce31..98427b67cd 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -716,6 +716,9 @@ ResetCatalogCaches(void)
  *	rather than relying on the relcache to keep a tupdesc for us.  Of course
  *	this assumes the tupdesc of a cachable system table will not change...)
  */
+/* CODE FOR catcachebench: REMOVE ME AFTER USE */
+bool _catcache_shrink_buckets = false;
+/* END: CODE FOR catcachebench*/
 void
 CatalogCacheFlushCatalog(Oid catId)
 {
@@ -735,6 +738,16 @@ CatalogCacheFlushCatalog(Oid catId)
 
 			/* Tell inval.c to call syscache callbacks for this cache */
 			CallSyscacheCallbacks(cache->id, 0);
+
+			/* CODE FOR catcachebench: REMOVE ME AFTER USE */
+			if (_catcache_shrink_buckets)
+			{
+				cache->cc_nbuckets = 128;
+				pfree(cache->cc_bucket);
+				cache->cc_bucket = palloc0(128 * sizeof(dlist_head));
+				elog(LOG, "Catcache reset");
+			}
+			/* END: CODE FOR catcachebench*/
 		}
 	}
 
-- 
2.16.3

----Next_Part(Mon_Jul_01_16_02_59_2019_106)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="v18-0003-Adjust-catcachebench-for-later-patches.patch"