On 2020-09-17 13:46, Michael Paquier wrote:
On Fri, Jul 31, 2020 at 06:47:48PM +0900, torikoshia wrote:
Oops, sorry about that.
I just fixed it there for now.

The regression tests of the patch look unstable, and the CF bot is
reporting a failure here:
https://travis-ci.org/github/postgresql-cfbot/postgresql/builds/727833416
--
Michael


Thank you for letting me know!


I'd like to reach a basic agreement on how we expose the
generic/custom plan information in pgss first.

Given the discussion so far, adding a new attribute to pgss key
is not appropriate since it can easily increase the number of
entries in pgss.

OTOH, just exposing the number of times generic/custom plan was
chosen seems not enough to know whether performance is degraded.

I'm now thinking about exposing not only the number of times
generic/custom plan was chosen but also some performance
metrics like 'total_time' for both generic and custom plans.

Attached a poc patch which exposes total, min, max, mean and
stddev time for both generic and custom plans.


  =# SELECT * FROM =# SELECT * FROM pg_stat_statements;
-[ RECORD 1 ]-------+---------------------------------------------------------
  userid              | 10
  dbid                | 12878
  queryid             | 4617094108938234366
query | PREPARE pr1 AS SELECT * FROM pg_class WHERE relname = $1
  plans               | 0
  total_plan_time     | 0
  min_plan_time       | 0
  max_plan_time       | 0
  mean_plan_time      | 0
  stddev_plan_time    | 0
  calls               | 6
  total_exec_time     | 0.46600699999999995
  min_exec_time       | 0.029376000000000003
  max_exec_time       | 0.237413
  mean_exec_time      | 0.07766783333333334
  stddev_exec_time    | 0.07254973134206326
  generic_calls       | 1
  total_generic_time  | 0.045334000000000006
  min_generic_time    | 0.045334000000000006
  max_generic_time    | 0.045334000000000006
  mean_generic_time   | 0.045334000000000006
  stddev_generic_time | 0
  custom_calls        | 5
  total_custom_time   | 0.42067299999999996
  min_custom_time     | 0.029376000000000003
  max_custom_time     | 0.237413
  mean_custom_time    | 0.0841346
  stddev_custom_time  | 0.07787966226583164
  ...

In this patch, exposing new columns is mandatory, but I think
it's better to make it optional by adding a GUC something
like 'pgss.track_general_custom_plans.

I also feel it makes the number of columns too many.
Just adding the total time may be sufficient.


Any thoughts?


Regards,

--
Atsushi Torikoshi
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql b/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql
index 0f63f08f7e..d118422b2a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql
@@ -29,6 +29,18 @@ CREATE FUNCTION pg_stat_statements(IN showtext boolean,
     OUT max_exec_time float8,
     OUT mean_exec_time float8,
     OUT stddev_exec_time float8,
+    OUT generic_calls int8,
+    OUT total_generic_time float8,
+    OUT min_generic_time float8,
+    OUT max_generic_time float8,
+    OUT mean_generic_time float8,
+    OUT stddev_generic_time float8,
+    OUT custom_calls int8,
+    OUT total_custom_time float8,
+    OUT min_custom_time float8,
+    OUT max_custom_time float8,
+    OUT mean_custom_time float8,
+    OUT stddev_custom_time float8,
     OUT rows int8,
     OUT shared_blks_hit int8,
     OUT shared_blks_read int8,
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 1eac9edaee..73d82a632d 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -78,9 +78,11 @@
 #include "storage/ipc.h"
 #include "storage/spin.h"
 #include "tcop/utility.h"
+#include "tcop/pquery.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
+#include "utils/plancache.h"
 
 PG_MODULE_MAGIC;
 
@@ -138,6 +140,8 @@ typedef enum pgssStoreKind
 	 */
 	PGSS_PLAN = 0,
 	PGSS_EXEC,
+	PGSS_EXEC_GENERAL,
+	PGSS_EXEC_CUSTOM,
 
 	PGSS_NUMKIND				/* Must be last value of this enum */
 } pgssStoreKind;
@@ -258,6 +262,13 @@ typedef struct pgssJumbleState
 	int			highest_extern_param_id;
 } pgssJumbleState;
 
+typedef enum pgssPlanType
+{
+	PGSS_PLAN_TYPE_NONE,
+	PGSS_PLAN_TYPE_GENERIC,
+	PGSS_PLAN_TYPE_CUSTOM,
+} pgssPlanType;
+
 /*---- Local variables ----*/
 
 /* Current nesting depth of ExecutorRun+ProcessUtility calls */
@@ -266,6 +277,9 @@ static int	exec_nested_level = 0;
 /* Current nesting depth of planner calls */
 static int	plan_nested_level = 0;
 
+/* Current plan type */
+static pgssPlanType	plantype = PGSS_PLAN_TYPE_NONE;
+
 /* Saved hook values in case of unload */
 static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
 static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
@@ -1013,6 +1027,20 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
 	 */
 	if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
 	{
+		/*
+		 * Since ActivePortal is not available at ExecutorEnd, We
+		 * preserve the plan type here.
+		 */
+		Assert(ActivePortal);
+
+		if (ActivePortal->cplan)
+		{
+			if (ActivePortal->cplan->is_generic)
+				plantype = PGSS_PLAN_TYPE_GENERIC;
+			else
+				plantype = PGSS_PLAN_TYPE_CUSTOM;
+		}
+
 		/*
 		 * Set up to track total elapsed time in ExecutorRun.  Make sure the
 		 * space is allocated in the per-query context so it will go away at
@@ -1100,6 +1128,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   &queryDesc->totaltime->walusage,
 				   NULL);
 	}
+	/* Reset the plan type. */
+	plantype = PGSS_PLAN_TYPE_NONE;
 
 	if (prev_ExecutorEnd)
 		prev_ExecutorEnd(queryDesc);
@@ -1224,6 +1254,41 @@ pgss_hash_string(const char *str, int len)
 											len, 0));
 }
 
+/* Note: Caller must acquire SpinLock on pgssEntry before using this function. */
+static void
+kind_dependent_accumulation(volatile pgssEntry *e, pgssStoreKind kind, double total_time)
+{
+	e->counters.calls[kind] += 1;
+	e->counters.total_time[kind] += total_time;
+
+	if (e->counters.calls[kind] == 1)
+	{
+		e->counters.min_time[kind] = total_time;
+		e->counters.max_time[kind] = total_time;
+		e->counters.mean_time[kind] = total_time;
+	}
+	else
+	{
+		/*
+		 * Welford's method for accurately computing variance. See
+		 * <http://www.johndcook.com/blog/standard_deviation/>
+		 */
+		double		old_mean = e->counters.mean_time[kind];
+
+		e->counters.mean_time[kind] +=
+			(total_time - old_mean) / e->counters.calls[kind];
+		e->counters.sum_var_time[kind] +=
+			(total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
+
+		/* calculate min and max time */
+		if (e->counters.min_time[kind] > total_time)
+			e->counters.min_time[kind] = total_time;
+		if (e->counters.max_time[kind] < total_time)
+			e->counters.max_time[kind] = total_time;
+	}
+}
+
+
 /*
  * Store some statistics for a statement.
  *
@@ -1396,34 +1461,20 @@ pgss_store(const char *query, uint64 queryId,
 		if (IS_STICKY(e->counters))
 			e->counters.usage = USAGE_INIT;
 
-		e->counters.calls[kind] += 1;
-		e->counters.total_time[kind] += total_time;
+		kind_dependent_accumulation(e, kind, total_time);
 
-		if (e->counters.calls[kind] == 1)
-		{
-			e->counters.min_time[kind] = total_time;
-			e->counters.max_time[kind] = total_time;
-			e->counters.mean_time[kind] = total_time;
-		}
-		else
+		/*
+		 * Accumulate information about general and custom plan.
+		 * TODO: It would be better to be optional.
+		 */
+		if (kind == PGSS_EXEC)
 		{
-			/*
-			 * Welford's method for accurately computing variance. See
-			 * <http://www.johndcook.com/blog/standard_deviation/>
-			 */
-			double		old_mean = e->counters.mean_time[kind];
-
-			e->counters.mean_time[kind] +=
-				(total_time - old_mean) / e->counters.calls[kind];
-			e->counters.sum_var_time[kind] +=
-				(total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
-
-			/* calculate min and max time */
-			if (e->counters.min_time[kind] > total_time)
-				e->counters.min_time[kind] = total_time;
-			if (e->counters.max_time[kind] < total_time)
-				e->counters.max_time[kind] = total_time;
+			if (plantype == PGSS_PLAN_TYPE_GENERIC)
+				kind_dependent_accumulation(e, PGSS_EXEC_GENERAL, total_time);
+			else if (plantype == PGSS_PLAN_TYPE_CUSTOM)
+				kind_dependent_accumulation(e, PGSS_EXEC_CUSTOM, total_time);
 		}
+
 		e->counters.rows += rows;
 		e->counters.shared_blks_hit += bufusage->shared_blks_hit;
 		e->counters.shared_blks_read += bufusage->shared_blks_read;
@@ -1488,8 +1539,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_1	18
 #define PG_STAT_STATEMENTS_COLS_V1_2	19
 #define PG_STAT_STATEMENTS_COLS_V1_3	23
-#define PG_STAT_STATEMENTS_COLS_V1_8	32
-#define PG_STAT_STATEMENTS_COLS			32	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_8	44
+#define PG_STAT_STATEMENTS_COLS			44	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 50d6ad28b4..47dc74a634 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -1217,12 +1217,14 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 		plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
 		/* Accumulate total costs of custom plans */
 		plansource->total_custom_cost += cached_plan_cost(plan, true);
+		plan->is_generic = false;
 
 		plansource->num_custom_plans++;
 	}
 	else
 	{
 		plansource->num_generic_plans++;
+		plan->is_generic = true;
 	}
 
 	Assert(plan != NULL);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 4901568553..4edc15602a 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -158,6 +158,7 @@ typedef struct CachedPlan
 	int			generation;		/* parent's generation number for this plan */
 	int			refcount;		/* count of live references to this struct */
 	MemoryContext context;		/* context containing this CachedPlan */
+	bool		is_generic;	/* is this plan generic or not? */
 } CachedPlan;
 
 /*

Reply via email to