Add parallel columns for pg_stat_statements

Started by Guillaume Lelargeover 1 year ago11 messages
#1Guillaume Lelarge
guillaume@lelarge.info
2 attachment(s)

Hello,

This patch was a bit discussed on [1]/messages/by-id/b4220d15-2e21-0e98-921b-b9892543cc93@dalibo.com, and with more details on [2]/messages/by-id/d657df20-c4bf-63f6-e74c-cb85a81d0383@dalibo.com. It's
based on another patch sent in 2022 (see [3]/messages/by-id/6acbe570-068e-bd8e-95d5-00c737b865e8@gmail.com). It introduces seven new
columns in pg_stat_statements:

* parallelized_queries_planned, number of times the query has been planned
to be parallelized,
* parallelized_queries_launched, number of times the query has been
executed with parallelization,
* parallelized_workers_planned, number of parallel workers planned for
this query,
* parallelized_workers_launched, number of parallel workers executed for
this query,
* parallelized_nodes, number of parallelized nodes,
* parallelized_nodes_all_workers, number of parallelized nodes which had
all requested workers,
* parallelized_nodes_no_worker, number of parallelized nodes which had no
requested workers.

As Benoit said yesterday, the intent is to help administrators evaluate the
usage of parallel workers in their databases and help configuring
parallelization usage.

A test script (test2.sql) is attached. You can execute it with "psql -Xef
test2.sql your_database" (your_database should not contain a t1 table as it
will be dropped and recreated).

Here is its result, a bit commented:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
-------------------------------
2024-08-29 18:00:35.314557+02
(1 row)

DROP TABLE IF EXISTS t1;
DROP TABLE
CREATE TABLE t1 (id integer);
CREATE TABLE
INSERT INTO t1 SELECT generate_series(1, 10_000_000);
INSERT 0 10000000
VACUUM ANALYZE t1;
VACUUM
SELECT query,
parallelized_queries_planned, parallelized_queries_launched,
parallelized_workers_planned, parallelized_workers_launched,
parallelized_nodes, parallelized_nodes_all_workers,
parallelized_nodes_no_worker
FROM pg_stat_statements
WHERE query LIKE 'SELECT%t1%'
(0 rows)

SELECT * FROM t1 LIMIT 1;
id
----
1
(1 row)

SELECT pg_sleep(1);
SELECT query,
parallelized_queries_planned, parallelized_queries_launched,
parallelized_workers_planned, parallelized_workers_launched,
parallelized_nodes, parallelized_nodes_all_workers,
parallelized_nodes_no_worker
FROM pg_stat_statements
WHERE query LIKE 'SELECT%t1%'
-[ RECORD 1 ]------------------+--------------------------
query | SELECT * FROM t1 LIMIT $1
parallelized_queries_planned | 0
parallelized_queries_launched | 0
parallelized_workers_planned | 0
parallelized_workers_launched | 0
parallelized_nodes | 0
parallelized_nodes_all_workers | 0
parallelized_nodes_no_worker | 0

==> no parallelization

SELECT count(*) FROM t1;
count
----------
10000000
(1 row)

SELECT pg_sleep(1);
SELECT query,
parallelized_queries_planned, parallelized_queries_launched,
parallelized_workers_planned, parallelized_workers_launched,
parallelized_nodes, parallelized_nodes_all_workers,
parallelized_nodes_no_worker
FROM pg_stat_statements
WHERE query LIKE 'SELECT%t1%'
-[ RECORD 1 ]------------------+--------------------------
query | SELECT count(*) FROM t1
parallelized_queries_planned | 1
parallelized_queries_launched | 1
parallelized_workers_planned | 2
parallelized_workers_launched | 2
parallelized_nodes | 1
parallelized_nodes_all_workers | 1
parallelized_nodes_no_worker | 0
-[ RECORD 2 ]------------------+--------------------------
query | SELECT * FROM t1 LIMIT $1
parallelized_queries_planned | 0
parallelized_queries_launched | 0
parallelized_workers_planned | 0
parallelized_workers_launched | 0
parallelized_nodes | 0
parallelized_nodes_all_workers | 0
parallelized_nodes_no_worker | 0

==> one parallelized query
==> I have the default configuration, so 2 for
max_parallel_worker_per_gather
==> hence two workers, with one node with all workers

SET max_parallel_workers_per_gather TO 5;
SET
SELECT count(*) FROM t1;
count
----------
10000000
(1 row)

SELECT pg_sleep(1);
SELECT query,
parallelized_queries_planned, parallelized_queries_launched,
parallelized_workers_planned, parallelized_workers_launched,
parallelized_nodes, parallelized_nodes_all_workers,
parallelized_nodes_no_worker
FROM pg_stat_statements
WHERE query LIKE 'SELECT%t1%'
-[ RECORD 1 ]------------------+--------------------------
query | SELECT count(*) FROM t1
parallelized_queries_planned | 2
parallelized_queries_launched | 2
parallelized_workers_planned | 6
parallelized_workers_launched | 6
parallelized_nodes | 2
parallelized_nodes_all_workers | 2
parallelized_nodes_no_worker | 0
-[ RECORD 2 ]------------------+--------------------------
query | SELECT * FROM t1 LIMIT $1
parallelized_queries_planned | 0
parallelized_queries_launched | 0
parallelized_workers_planned | 0
parallelized_workers_launched | 0
parallelized_nodes | 0
parallelized_nodes_all_workers | 0
parallelized_nodes_no_worker | 0

==> another parallelized query
==> with 5 as max_parallel_workers_per_gather, but only 4 workers to use
==> hence four workers, with one node with all workers

The biggest issue with this patch is that it's unable to know workers for
maintenance queries (CREATE INDEX for BTree and VACUUM).

Documentation is done, tests are missing. Once there's an agreement on this
patch, we'll work on the tests.

This has been a collective work with Benoit Lobréau, Jehan-Guillaume de
Rorthais, and Franck Boudehen.

Thanks.

Regards.

[1]: /messages/by-id/b4220d15-2e21-0e98-921b-b9892543cc93@dalibo.com
/messages/by-id/b4220d15-2e21-0e98-921b-b9892543cc93@dalibo.com
[2]: /messages/by-id/d657df20-c4bf-63f6-e74c-cb85a81d0383@dalibo.com
/messages/by-id/d657df20-c4bf-63f6-e74c-cb85a81d0383@dalibo.com
[3]: /messages/by-id/6acbe570-068e-bd8e-95d5-00c737b865e8@gmail.com
/messages/by-id/6acbe570-068e-bd8e-95d5-00c737b865e8@gmail.com

--
Guillaume.

Attachments:

0001-Add-parallel-columns-to-pg_stat_statements.patchtext/x-patch; charset=US-ASCII; name=0001-Add-parallel-columns-to-pg_stat_statements.patchDownload
From 59fd586cac8f0bafb1fa66548424f2dab0b38f31 Mon Sep 17 00:00:00 2001
From: Guillaume Lelarge <guillaume.lelarge@dalibo.com>
Date: Wed, 28 Aug 2024 15:30:05 +0200
Subject: [PATCH] Add parallel columns to pg_stat_statements

There are seven new columns:
 * parallelized_queries_planned (number of times the query has been planned to
   be parallelized),
 * parallel_ized_querieslaunched (number of times the query has been executed
   with parallelization),
 * parallelized_workers_planned (number of parallel workers planned for
   this query),
 * parallelized_workers_launched (number of parallel workers executed for
   this query),
 * parallelized_nodes (number of parallelized nodes),
 * parallelized_nodes_all_workers (number of parallelized nodes which
   had all requested workers),
 * parallelized_nodes_no_worker (number of parallelized nodes which had
   no requested workers).

These new columns will help to monitor and better configure query
parallelization.
---
 contrib/pg_stat_statements/Makefile           |   2 +-
 .../pg_stat_statements--1.11--1.12.sql        |  80 +++++++++++++
 .../pg_stat_statements/pg_stat_statements.c   | 108 ++++++++++++++++--
 .../pg_stat_statements.control                |   2 +-
 doc/src/sgml/pgstatstatements.sgml            |  63 ++++++++++
 src/backend/executor/execUtils.c              |   7 ++
 src/backend/executor/nodeGather.c             |   9 +-
 src/backend/executor/nodeGatherMerge.c        |   8 ++
 src/include/nodes/execnodes.h                 |   6 +
 9 files changed, 275 insertions(+), 10 deletions(-)
 create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql

diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index c19ccad77e..62f8df65b5 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -7,7 +7,7 @@ OBJS = \
 
 EXTENSION = pg_stat_statements
 DATA = pg_stat_statements--1.4.sql \
-	pg_stat_statements--1.10--1.11.sql \
+	pg_stat_statements--1.11--1.12.sql pg_stat_statements--1.10--1.11.sql \
 	pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
 	pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
 	pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
new file mode 100644
index 0000000000..6f4fe8be48
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
@@ -0,0 +1,80 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.12'" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements;
+ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements(boolean);
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+    OUT userid oid,
+    OUT dbid oid,
+    OUT toplevel bool,
+    OUT queryid bigint,
+    OUT query text,
+    OUT plans int8,
+    OUT total_plan_time float8,
+    OUT min_plan_time float8,
+    OUT max_plan_time float8,
+    OUT mean_plan_time float8,
+    OUT stddev_plan_time float8,
+    OUT calls int8,
+    OUT total_exec_time float8,
+    OUT min_exec_time float8,
+    OUT max_exec_time float8,
+    OUT mean_exec_time float8,
+    OUT stddev_exec_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8,
+    OUT shared_blk_read_time float8,
+    OUT shared_blk_write_time float8,
+    OUT local_blk_read_time float8,
+    OUT local_blk_write_time float8,
+    OUT temp_blk_read_time float8,
+    OUT temp_blk_write_time float8,
+    OUT wal_records int8,
+    OUT wal_fpi int8,
+    OUT wal_bytes numeric,
+    OUT jit_functions int8,
+    OUT jit_generation_time float8,
+    OUT jit_inlining_count int8,
+    OUT jit_inlining_time float8,
+    OUT jit_optimization_count int8,
+    OUT jit_optimization_time float8,
+    OUT jit_emission_count int8,
+    OUT jit_emission_time float8,
+    OUT jit_deform_count int8,
+    OUT jit_deform_time float8,
+    OUT parallelized_queries_planned int8,
+    OUT parallelized_queries_launched int8,
+    OUT parallelized_workers_planned int8,
+    OUT parallelized_workers_launched int8,
+    OUT parallelized_nodes int8,
+    OUT parallelized_nodes_all_workers int8,
+    OUT parallelized_nodes_no_worker int8,
+    OUT stats_since timestamp with time zone,
+    OUT minmax_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_12'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 362d222f63..840ab0bccd 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -113,6 +113,7 @@ typedef enum pgssVersion
 	PGSS_V1_9,
 	PGSS_V1_10,
 	PGSS_V1_11,
+	PGSS_V1_12,
 } pgssVersion;
 
 typedef enum pgssStoreKind
@@ -204,6 +205,13 @@ typedef struct Counters
 	int64		jit_emission_count; /* number of times emission time has been
 									 * > 0 */
 	double		jit_emission_time;	/* total time to emit jit code */
+	int64       parallelized_queries_planned;	/* # of times query was planned to use parallelism */
+	int64       parallelized_queries_launched;	/* # of times query was executed using parallelism */
+	int64       parallelized_workers_planned;	/* # of parallel workers planned */
+	int64       parallelized_workers_launched;	/* # of parallel workers launched */
+	int64       parallelized_nodes;				/* # of parallelized nodes */
+	int64       parallelized_nodes_all_workers;	/* # of parallelized nodes with all workers */
+	int64       parallelized_nodes_no_worker;	/* # of parallelized nodes with no workers */
 } Counters;
 
 /*
@@ -317,6 +325,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
 PG_FUNCTION_INFO_V1(pg_stat_statements);
 PG_FUNCTION_INFO_V1(pg_stat_statements_info);
 
@@ -347,7 +356,14 @@ static void pgss_store(const char *query, uint64 queryId,
 					   const BufferUsage *bufusage,
 					   const WalUsage *walusage,
 					   const struct JitInstrumentation *jitusage,
-					   JumbleState *jstate);
+					   JumbleState *jstate,
+					   bool parallelized_queries_planned,
+					   bool parallelized_queries_launched,
+					   int parallelized_workers_planned,
+					   int parallelized_workers_launched,
+					   int parallelized_nodes,
+					   int parallelized_nodes_all_workers,
+					   int parallelized_nodes_no_worker);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 										pgssVersion api_version,
 										bool showtext);
@@ -867,7 +883,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
 				   NULL,
 				   NULL,
 				   NULL,
-				   jstate);
+				   jstate,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 }
 
 /*
@@ -952,7 +975,14 @@ pgss_planner(Query *parse,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1085,7 +1115,14 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   &queryDesc->totaltime->bufusage,
 				   &queryDesc->totaltime->walusage,
 				   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
-				   NULL);
+				   NULL,
+				   queryDesc->plannedstmt->parallelModeNeeded,
+				   queryDesc->estate->es_used_parallel_mode,
+				   queryDesc->estate->es_parallelized_workers_planned,
+				   queryDesc->estate->es_parallelized_workers_launched,
+				   queryDesc->estate->es_parallelized_nodes,
+				   queryDesc->estate->es_parallelized_nodes_all_workers,
+				   queryDesc->estate->es_parallelized_nodes_no_worker);
 	}
 
 	if (prev_ExecutorEnd)
@@ -1216,7 +1253,14 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1277,7 +1321,14 @@ pgss_store(const char *query, uint64 queryId,
 		   const BufferUsage *bufusage,
 		   const WalUsage *walusage,
 		   const struct JitInstrumentation *jitusage,
-		   JumbleState *jstate)
+		   JumbleState *jstate,
+		   bool parallelized_queries_planned,
+		   bool parallelized_queries_launched,
+		   int parallelized_workers_planned,
+		   int parallelized_workers_launched,
+		   int parallelized_nodes,
+		   int parallelized_nodes_all_workers,
+		   int parallelized_nodes_no_worker)
 {
 	pgssHashKey key;
 	pgssEntry  *entry;
@@ -1479,6 +1530,20 @@ pgss_store(const char *query, uint64 queryId,
 				entry->counters.jit_emission_count++;
 			entry->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
 		}
+		/* inc parallel counters */
+		if (parallelized_queries_planned)
+		{
+			entry->counters.parallelized_queries_planned += 1;
+		}
+		if (parallelized_queries_launched)
+		{
+			entry->counters.parallelized_queries_launched += 1;
+		}
+		entry->counters.parallelized_workers_planned += parallelized_workers_planned;
+		entry->counters.parallelized_workers_launched += parallelized_workers_launched;
+		entry->counters.parallelized_nodes += parallelized_nodes;
+		entry->counters.parallelized_nodes_all_workers += parallelized_nodes_all_workers;
+		entry->counters.parallelized_nodes_no_worker += parallelized_nodes_no_worker;
 
 		SpinLockRelease(&entry->mutex);
 	}
@@ -1546,7 +1611,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_9	33
 #define PG_STAT_STATEMENTS_COLS_V1_10	43
 #define PG_STAT_STATEMENTS_COLS_V1_11	49
-#define PG_STAT_STATEMENTS_COLS			49	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_12	56
+#define PG_STAT_STATEMENTS_COLS			56	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1558,6 +1624,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
  * expected API version is identified by embedding it in the C name of the
  * function.  Unfortunately we weren't bright enough to do that for 1.1.
  */
+Datum
+pg_stat_statements_1_12(PG_FUNCTION_ARGS)
+{
+	bool		showtext = PG_GETARG_BOOL(0);
+
+	pg_stat_statements_internal(fcinfo, PGSS_V1_12, showtext);
+
+	return (Datum) 0;
+}
+
 Datum
 pg_stat_statements_1_11(PG_FUNCTION_ARGS)
 {
@@ -1702,6 +1778,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 			if (api_version != PGSS_V1_11)
 				elog(ERROR, "incorrect number of output arguments");
 			break;
+		case PG_STAT_STATEMENTS_COLS_V1_12:
+			if (api_version != PGSS_V1_12)
+				elog(ERROR, "incorrect number of output arguments");
+			break;
 		default:
 			elog(ERROR, "incorrect number of output arguments");
 	}
@@ -1939,6 +2019,19 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 		{
 			values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
 			values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
+		}
+		if (api_version >= PGSS_V1_12)
+		{
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes_all_workers);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes_no_worker);
+		}
+		if (api_version >= PGSS_V1_11)
+		{
 			values[i++] = TimestampTzGetDatum(stats_since);
 			values[i++] = TimestampTzGetDatum(minmax_stats_since);
 		}
@@ -1951,6 +2044,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 					 api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
 					 api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
 					 api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
+					 api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
 					 -1 /* fail if you forget to update this assert */ ));
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 8a76106ec6..d45ebc12e3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.11'
+default_version = '1.12'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 9b0aff73b1..3fe84ba994 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -527,6 +527,69 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_queries_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement was planned to use parallelism.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_queries_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times that the statement was executed using parallelism
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_workers_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers planned for the query.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_workers_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers executed for the query.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes_all_workers</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes that got all workers
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes_no_worker</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes that got no worker at all
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5737f9f4eb..3b0715d604 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -158,10 +158,17 @@ CreateExecutorState(void)
 	estate->es_sourceText = NULL;
 
 	estate->es_use_parallel_mode = false;
+	estate->es_used_parallel_mode = false;
 
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
 
+	estate->es_parallelized_nodes = 0;
+	estate->es_parallelized_nodes_all_workers = 0;
+	estate->es_parallelized_nodes_no_worker = 0;
+	estate->es_parallelized_workers_launched = 0;
+	estate->es_parallelized_workers_planned = 0;
+
 	/*
 	 * Return the executor state structure
 	 */
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 5d4ffe989c..f1f772ecb6 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -181,7 +181,13 @@ ExecGather(PlanState *pstate)
 			LaunchParallelWorkers(pcxt);
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
-
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
+			estate->es_parallelized_nodes += 1;
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+			if (pcxt->nworkers_to_launch == pcxt->nworkers_launched)
+				estate->es_parallelized_nodes_all_workers += 1;
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
@@ -198,6 +204,7 @@ ExecGather(PlanState *pstate)
 				/* No workers?	Then never mind. */
 				node->nreaders = 0;
 				node->reader = NULL;
+				estate->es_parallelized_nodes_no_worker += 1;
 			}
 			node->nextreader = 0;
 		}
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 45f6017c29..5678b25a86 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -222,6 +222,13 @@ ExecGatherMerge(PlanState *pstate)
 			LaunchParallelWorkers(pcxt);
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
+			estate->es_parallelized_nodes += 1;
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+			if (pcxt->nworkers_to_launch == pcxt->nworkers_launched)
+				estate->es_parallelized_nodes_all_workers += 1;
 
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
@@ -239,6 +246,7 @@ ExecGatherMerge(PlanState *pstate)
 				/* No workers?	Then never mind. */
 				node->nreaders = 0;
 				node->reader = NULL;
+				estate->es_parallelized_nodes_no_worker += 1;
 			}
 		}
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index af7d8fd1e7..26baa444a2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -701,6 +701,12 @@ typedef struct EState
 	struct EPQState *es_epq_active;
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
+	bool		es_used_parallel_mode;	/* was executed in parallel */
+	int			es_parallelized_workers_launched;
+	int			es_parallelized_workers_planned;
+	int			es_parallelized_nodes; /* # of parallelized nodes */
+	int			es_parallelized_nodes_all_workers; /* # of nodes with all workers launched */
+	int			es_parallelized_nodes_no_worker; /* # of nodes with no workers launched */
 
 	/* The per-query shared memory area to use for parallel execution. */
 	struct dsa_area *es_query_dsa;
-- 
2.46.0

test2.sqlapplication/sql; name=test2.sqlDownload
#2Michael Paquier
michael@paquier.xyz
In reply to: Guillaume Lelarge (#1)
Re: Add parallel columns for pg_stat_statements

On Thu, Aug 29, 2024 at 10:08:23PM +0200, Guillaume Lelarge wrote:

This patch was a bit discussed on [1], and with more details on [2]. It's
based on another patch sent in 2022 (see [3]). It introduces seven new
columns in pg_stat_statements:

* parallelized_queries_planned, number of times the query has been planned
to be parallelized,
* parallelized_queries_launched, number of times the query has been
executed with parallelization,

Comparing the numbers of workers planned and launched with the number
of times a query has been called and planned should provide a rather
good equivalent, no? I am not sure that these two are mandatory to
have.

* parallelized_workers_planned, number of parallel workers planned for
this query,
* parallelized_workers_launched, number of parallel workers executed for
this query,

Yep. Definitely OK with these two. There is an overlap with what
Benoit has sent here when it comes to publish this data to the
executor state:
/messages/by-id/783bc7f7-659a-42fa-99dd-ee0565644e25@dalibo.com

* parallelized_nodes, number of parallelized nodes,
* parallelized_nodes_all_workers, number of parallelized nodes which had
all requested workers,

* parallelized_nodes_no_worker, number of parallelized nodes which had no
requested workers.

I can see why you want to register this extra data on a node-basis,
but how does that help when it comes to tune the parallel GUCs? We
cannot control them at node level and the launched/planned ratio
offers an equivalent of that. Not exactly, but that's enough to get a
picture if there is a draught.

A test script (test2.sql) is attached. You can execute it with "psql -Xef
test2.sql your_database" (your_database should not contain a t1 table as it
will be dropped and recreated).

Let's add proper regression tests instead, including
oldextversions.sql as this bumps the version of the module. See for
example the tests of 6fd5071909a2 that can force workers to spawn
for BRIN and btree queries, validating some of the stats published
here.
--
Michael

#3Guillaume Lelarge
guillaume@lelarge.info
In reply to: Michael Paquier (#2)
1 attachment(s)
Re: Add parallel columns for pg_stat_statements

Hi Michael,

Le jeu. 3 oct. 2024 à 09:15, Michael Paquier <michael@paquier.xyz> a écrit :

On Thu, Aug 29, 2024 at 10:08:23PM +0200, Guillaume Lelarge wrote:

This patch was a bit discussed on [1], and with more details on [2]. It's
based on another patch sent in 2022 (see [3]). It introduces seven new
columns in pg_stat_statements:

* parallelized_queries_planned, number of times the query has been

planned

to be parallelized,
* parallelized_queries_launched, number of times the query has been
executed with parallelization,

Comparing the numbers of workers planned and launched with the number
of times a query has been called and planned should provide a rather
good equivalent, no? I am not sure that these two are mandatory to
have.

I'm not sure I follow. That would mean that every time a query is executed,
it always gets the same amount of workers. Which is not guaranteed to be
true.

I would agree, though, that parallelized_queries_launched is probably not
that interesting. I could get rid of it if you think it should go away.

* parallelized_workers_planned, number of parallel workers planned for

this query,
* parallelized_workers_launched, number of parallel workers executed for
this query,

Yep. Definitely OK with these two. There is an overlap with what
Benoit has sent here when it comes to publish this data to the
executor state:

/messages/by-id/783bc7f7-659a-42fa-99dd-ee0565644e25@dalibo.com

Well, I don't see this as an overlap. Rather more information.

* parallelized_nodes, number of parallelized nodes,
* parallelized_nodes_all_workers, number of parallelized nodes which had
all requested workers,

* parallelized_nodes_no_worker, number of parallelized nodes which had

no

requested workers.

I can see why you want to register this extra data on a node-basis,
but how does that help when it comes to tune the parallel GUCs? We
cannot control them at node level and the launched/planned ratio
offers an equivalent of that. Not exactly, but that's enough to get a
picture if there is a draught.

On this, I would agree with you. They are not that particularly useful to
get better setting for parallel GUCs. I can drop them if you want.

A test script (test2.sql) is attached. You can execute it with "psql -Xef

test2.sql your_database" (your_database should not contain a t1 table as

it

will be dropped and recreated).

Let's add proper regression tests instead, including
oldextversions.sql as this bumps the version of the module. See for
example the tests of 6fd5071909a2 that can force workers to spawn
for BRIN and btree queries, validating some of the stats published
here.

Did this on the v2 version of the patch (attached here).

Thanks for your review. If you want the parallelized_queries_launched
column and the parallelized_nodes_* columns dropped, I can do that on a v3
patch.

Regards.

--
Guillaume.

Attachments:

v2-0001-Add-parallel-columns-to-pg_stat_statements.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Add-parallel-columns-to-pg_stat_statements.patchDownload
From 8228f8d521eb9853cba6b5d185e4d9965f29d398 Mon Sep 17 00:00:00 2001
From: Guillaume Lelarge <guillaume.lelarge@dalibo.com>
Date: Wed, 28 Aug 2024 15:30:05 +0200
Subject: [PATCH v2] Add parallel columns to pg_stat_statements

There are seven new columns:
 * parallelized_queries_planned (number of times the query has been planned to
   be parallelized),
 * parallel_ized_querieslaunched (number of times the query has been executed
   with parallelization),
 * parallelized_workers_planned (number of parallel workers planned for
   this query),
 * parallelized_workers_launched (number of parallel workers executed for
   this query),
 * parallelized_nodes (number of parallelized nodes),
 * parallelized_nodes_all_workers (number of parallelized nodes which
   had all requested workers),
 * parallelized_nodes_no_worker (number of parallelized nodes which had
   no requested workers).

These new columns will help to monitor and better configure query
parallelization.
---
 contrib/pg_stat_statements/Makefile           |   4 +-
 .../expected/oldextversions.out               |  69 +++++++++++
 .../pg_stat_statements/expected/parallel.out  |  38 ++++++
 .../pg_stat_statements--1.11--1.12.sql        |  80 +++++++++++++
 .../pg_stat_statements/pg_stat_statements.c   | 108 ++++++++++++++++--
 .../pg_stat_statements.control                |   2 +-
 .../pg_stat_statements/sql/oldextversions.sql |   5 +
 contrib/pg_stat_statements/sql/parallel.sql   |  27 +++++
 doc/src/sgml/pgstatstatements.sgml            |  63 ++++++++++
 src/backend/executor/execUtils.c              |   7 ++
 src/backend/executor/nodeGather.c             |   9 +-
 src/backend/executor/nodeGatherMerge.c        |   8 ++
 src/include/nodes/execnodes.h                 |   6 +
 13 files changed, 415 insertions(+), 11 deletions(-)
 create mode 100644 contrib/pg_stat_statements/expected/parallel.out
 create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
 create mode 100644 contrib/pg_stat_statements/sql/parallel.sql

diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 1622b43ded..0caeea0c03 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -7,7 +7,7 @@ OBJS = \
 
 EXTENSION = pg_stat_statements
 DATA = pg_stat_statements--1.4.sql \
-	pg_stat_statements--1.10--1.11.sql \
+	pg_stat_statements--1.11--1.12.sql pg_stat_statements--1.10--1.11.sql \
 	pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
 	pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
 	pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -19,7 +19,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
 
 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
 REGRESS = select dml cursors utility level_tracking planning \
-	user_activity wal entry_timestamp privileges extended cleanup \
+	user_activity wal entry_timestamp privileges extended parallel cleanup \
 	oldextversions
 # Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
 # which typical installcheck users do not have (e.g. buildfarm clients).
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5842c930e5..a4e8dc0bde 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -342,4 +342,73 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
  t
 (1 row)
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+                              View "public.pg_stat_statements"
+             Column             |           Type           | Collation | Nullable | Default 
+--------------------------------+--------------------------+-----------+----------+---------
+ userid                         | oid                      |           |          | 
+ dbid                           | oid                      |           |          | 
+ toplevel                       | boolean                  |           |          | 
+ queryid                        | bigint                   |           |          | 
+ query                          | text                     |           |          | 
+ plans                          | bigint                   |           |          | 
+ total_plan_time                | double precision         |           |          | 
+ min_plan_time                  | double precision         |           |          | 
+ max_plan_time                  | double precision         |           |          | 
+ mean_plan_time                 | double precision         |           |          | 
+ stddev_plan_time               | double precision         |           |          | 
+ calls                          | bigint                   |           |          | 
+ total_exec_time                | double precision         |           |          | 
+ min_exec_time                  | double precision         |           |          | 
+ max_exec_time                  | double precision         |           |          | 
+ mean_exec_time                 | double precision         |           |          | 
+ stddev_exec_time               | double precision         |           |          | 
+ rows                           | bigint                   |           |          | 
+ shared_blks_hit                | bigint                   |           |          | 
+ shared_blks_read               | bigint                   |           |          | 
+ shared_blks_dirtied            | bigint                   |           |          | 
+ shared_blks_written            | bigint                   |           |          | 
+ local_blks_hit                 | bigint                   |           |          | 
+ local_blks_read                | bigint                   |           |          | 
+ local_blks_dirtied             | bigint                   |           |          | 
+ local_blks_written             | bigint                   |           |          | 
+ temp_blks_read                 | bigint                   |           |          | 
+ temp_blks_written              | bigint                   |           |          | 
+ shared_blk_read_time           | double precision         |           |          | 
+ shared_blk_write_time          | double precision         |           |          | 
+ local_blk_read_time            | double precision         |           |          | 
+ local_blk_write_time           | double precision         |           |          | 
+ temp_blk_read_time             | double precision         |           |          | 
+ temp_blk_write_time            | double precision         |           |          | 
+ wal_records                    | bigint                   |           |          | 
+ wal_fpi                        | bigint                   |           |          | 
+ wal_bytes                      | numeric                  |           |          | 
+ jit_functions                  | bigint                   |           |          | 
+ jit_generation_time            | double precision         |           |          | 
+ jit_inlining_count             | bigint                   |           |          | 
+ jit_inlining_time              | double precision         |           |          | 
+ jit_optimization_count         | bigint                   |           |          | 
+ jit_optimization_time          | double precision         |           |          | 
+ jit_emission_count             | bigint                   |           |          | 
+ jit_emission_time              | double precision         |           |          | 
+ jit_deform_count               | bigint                   |           |          | 
+ jit_deform_time                | double precision         |           |          | 
+ parallelized_queries_planned   | bigint                   |           |          | 
+ parallelized_queries_launched  | bigint                   |           |          | 
+ parallelized_workers_planned   | bigint                   |           |          | 
+ parallelized_workers_launched  | bigint                   |           |          | 
+ parallelized_nodes             | bigint                   |           |          | 
+ parallelized_nodes_all_workers | bigint                   |           |          | 
+ parallelized_nodes_no_worker   | bigint                   |           |          | 
+ stats_since                    | timestamp with time zone |           |          | 
+ minmax_stats_since             | timestamp with time zone |           |          | 
+
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+ has_data 
+----------
+ t
+(1 row)
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/parallel.out b/contrib/pg_stat_statements/expected/parallel.out
new file mode 100644
index 0000000000..1379d10e97
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/parallel.out
@@ -0,0 +1,38 @@
+--
+-- Validate parallelization generation metrics
+--
+SET parallel_setup_cost TO 0;
+SET min_parallel_table_scan_size TO '100kB';
+SET pg_stat_statements.track_utility = FALSE;
+CREATE TABLE pgss_parallel_tab (a int);
+INSERT INTO pgss_parallel_tab SELECT generate_series(1, 10_000);
+SELECT count(*) FROM pgss_parallel_tab;
+ count 
+-------
+ 10000
+(1 row)
+
+DROP TABLE pgss_parallel_tab;
+-- Check parallelization metrics are generated for the SELECT statement
+SELECT query,
+  parallelized_queries_planned > 0 AS parallelized_queries_planned_generated,
+  parallelized_queries_launched > 0 AS parallelized_queries_launched_generated,
+  parallelized_workers_planned > 0 AS parallelized_workers_planned_generated,
+  parallelized_workers_launched > 0 AS parallelized_workers_launched_generated,
+  parallelized_nodes > 0 AS parallelized_nodes_generated,
+  parallelized_nodes_all_workers > 0 AS parallelized_nodes_all_workers_generated,
+  parallelized_nodes_no_worker = 0 AS parallelized_nodes_no_worker_generated 
+FROM pg_stat_statements
+WHERE query LIKE 'SELECT count%'
+ORDER BY query COLLATE "C";
+                 query                  | parallelized_queries_planned_generated | parallelized_queries_launched_generated | parallelized_workers_planned_generated | parallelized_workers_launched_generated | parallelized_nodes_generated | parallelized_nodes_all_workers_generated | parallelized_nodes_no_worker_generated 
+----------------------------------------+----------------------------------------+-----------------------------------------+----------------------------------------+-----------------------------------------+------------------------------+------------------------------------------+----------------------------------------
+ SELECT count(*) FROM pgss_parallel_tab | t                                      | t                                       | t                                      | t                                       | t                            | t                                        | t
+(1 row)
+
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
new file mode 100644
index 0000000000..6f4fe8be48
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
@@ -0,0 +1,80 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.12'" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements;
+ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements(boolean);
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+    OUT userid oid,
+    OUT dbid oid,
+    OUT toplevel bool,
+    OUT queryid bigint,
+    OUT query text,
+    OUT plans int8,
+    OUT total_plan_time float8,
+    OUT min_plan_time float8,
+    OUT max_plan_time float8,
+    OUT mean_plan_time float8,
+    OUT stddev_plan_time float8,
+    OUT calls int8,
+    OUT total_exec_time float8,
+    OUT min_exec_time float8,
+    OUT max_exec_time float8,
+    OUT mean_exec_time float8,
+    OUT stddev_exec_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8,
+    OUT shared_blk_read_time float8,
+    OUT shared_blk_write_time float8,
+    OUT local_blk_read_time float8,
+    OUT local_blk_write_time float8,
+    OUT temp_blk_read_time float8,
+    OUT temp_blk_write_time float8,
+    OUT wal_records int8,
+    OUT wal_fpi int8,
+    OUT wal_bytes numeric,
+    OUT jit_functions int8,
+    OUT jit_generation_time float8,
+    OUT jit_inlining_count int8,
+    OUT jit_inlining_time float8,
+    OUT jit_optimization_count int8,
+    OUT jit_optimization_time float8,
+    OUT jit_emission_count int8,
+    OUT jit_emission_time float8,
+    OUT jit_deform_count int8,
+    OUT jit_deform_time float8,
+    OUT parallelized_queries_planned int8,
+    OUT parallelized_queries_launched int8,
+    OUT parallelized_workers_planned int8,
+    OUT parallelized_workers_launched int8,
+    OUT parallelized_nodes int8,
+    OUT parallelized_nodes_all_workers int8,
+    OUT parallelized_nodes_no_worker int8,
+    OUT stats_since timestamp with time zone,
+    OUT minmax_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_12'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5765ef49b4..da94d64011 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -113,6 +113,7 @@ typedef enum pgssVersion
 	PGSS_V1_9,
 	PGSS_V1_10,
 	PGSS_V1_11,
+	PGSS_V1_12,
 } pgssVersion;
 
 typedef enum pgssStoreKind
@@ -204,6 +205,13 @@ typedef struct Counters
 	int64		jit_emission_count; /* number of times emission time has been
 									 * > 0 */
 	double		jit_emission_time;	/* total time to emit jit code */
+	int64       parallelized_queries_planned;	/* # of times query was planned to use parallelism */
+	int64       parallelized_queries_launched;	/* # of times query was executed using parallelism */
+	int64       parallelized_workers_planned;	/* # of parallel workers planned */
+	int64       parallelized_workers_launched;	/* # of parallel workers launched */
+	int64       parallelized_nodes;				/* # of parallelized nodes */
+	int64       parallelized_nodes_all_workers;	/* # of parallelized nodes with all workers */
+	int64       parallelized_nodes_no_worker;	/* # of parallelized nodes with no workers */
 } Counters;
 
 /*
@@ -317,6 +325,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
 PG_FUNCTION_INFO_V1(pg_stat_statements);
 PG_FUNCTION_INFO_V1(pg_stat_statements_info);
 
@@ -347,7 +356,14 @@ static void pgss_store(const char *query, uint64 queryId,
 					   const BufferUsage *bufusage,
 					   const WalUsage *walusage,
 					   const struct JitInstrumentation *jitusage,
-					   JumbleState *jstate);
+					   JumbleState *jstate,
+					   bool parallelized_queries_planned,
+					   bool parallelized_queries_launched,
+					   int parallelized_workers_planned,
+					   int parallelized_workers_launched,
+					   int parallelized_nodes,
+					   int parallelized_nodes_all_workers,
+					   int parallelized_nodes_no_worker);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 										pgssVersion api_version,
 										bool showtext);
@@ -867,7 +883,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
 				   NULL,
 				   NULL,
 				   NULL,
-				   jstate);
+				   jstate,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 }
 
 /*
@@ -945,7 +968,14 @@ pgss_planner(Query *parse,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1078,7 +1108,14 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   &queryDesc->totaltime->bufusage,
 				   &queryDesc->totaltime->walusage,
 				   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
-				   NULL);
+				   NULL,
+				   queryDesc->plannedstmt->parallelModeNeeded,
+				   queryDesc->estate->es_used_parallel_mode,
+				   queryDesc->estate->es_parallelized_workers_planned,
+				   queryDesc->estate->es_parallelized_workers_launched,
+				   queryDesc->estate->es_parallelized_nodes,
+				   queryDesc->estate->es_parallelized_nodes_all_workers,
+				   queryDesc->estate->es_parallelized_nodes_no_worker);
 	}
 
 	if (prev_ExecutorEnd)
@@ -1209,7 +1246,14 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0,
+				   0,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1270,7 +1314,14 @@ pgss_store(const char *query, uint64 queryId,
 		   const BufferUsage *bufusage,
 		   const WalUsage *walusage,
 		   const struct JitInstrumentation *jitusage,
-		   JumbleState *jstate)
+		   JumbleState *jstate,
+		   bool parallelized_queries_planned,
+		   bool parallelized_queries_launched,
+		   int parallelized_workers_planned,
+		   int parallelized_workers_launched,
+		   int parallelized_nodes,
+		   int parallelized_nodes_all_workers,
+		   int parallelized_nodes_no_worker)
 {
 	pgssHashKey key;
 	pgssEntry  *entry;
@@ -1472,6 +1523,20 @@ pgss_store(const char *query, uint64 queryId,
 				entry->counters.jit_emission_count++;
 			entry->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
 		}
+		/* inc parallel counters */
+		if (parallelized_queries_planned)
+		{
+			entry->counters.parallelized_queries_planned += 1;
+		}
+		if (parallelized_queries_launched)
+		{
+			entry->counters.parallelized_queries_launched += 1;
+		}
+		entry->counters.parallelized_workers_planned += parallelized_workers_planned;
+		entry->counters.parallelized_workers_launched += parallelized_workers_launched;
+		entry->counters.parallelized_nodes += parallelized_nodes;
+		entry->counters.parallelized_nodes_all_workers += parallelized_nodes_all_workers;
+		entry->counters.parallelized_nodes_no_worker += parallelized_nodes_no_worker;
 
 		SpinLockRelease(&entry->mutex);
 	}
@@ -1539,7 +1604,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_9	33
 #define PG_STAT_STATEMENTS_COLS_V1_10	43
 #define PG_STAT_STATEMENTS_COLS_V1_11	49
-#define PG_STAT_STATEMENTS_COLS			49	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_12	56
+#define PG_STAT_STATEMENTS_COLS			56	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1551,6 +1617,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
  * expected API version is identified by embedding it in the C name of the
  * function.  Unfortunately we weren't bright enough to do that for 1.1.
  */
+Datum
+pg_stat_statements_1_12(PG_FUNCTION_ARGS)
+{
+	bool		showtext = PG_GETARG_BOOL(0);
+
+	pg_stat_statements_internal(fcinfo, PGSS_V1_12, showtext);
+
+	return (Datum) 0;
+}
+
 Datum
 pg_stat_statements_1_11(PG_FUNCTION_ARGS)
 {
@@ -1695,6 +1771,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 			if (api_version != PGSS_V1_11)
 				elog(ERROR, "incorrect number of output arguments");
 			break;
+		case PG_STAT_STATEMENTS_COLS_V1_12:
+			if (api_version != PGSS_V1_12)
+				elog(ERROR, "incorrect number of output arguments");
+			break;
 		default:
 			elog(ERROR, "incorrect number of output arguments");
 	}
@@ -1932,6 +2012,19 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 		{
 			values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
 			values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
+		}
+		if (api_version >= PGSS_V1_12)
+		{
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes_all_workers);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_nodes_no_worker);
+		}
+		if (api_version >= PGSS_V1_11)
+		{
 			values[i++] = TimestampTzGetDatum(stats_since);
 			values[i++] = TimestampTzGetDatum(minmax_stats_since);
 		}
@@ -1944,6 +2037,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 					 api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
 					 api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
 					 api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
+					 api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
 					 -1 /* fail if you forget to update this assert */ ));
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 8a76106ec6..d45ebc12e3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.11'
+default_version = '1.12'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 38d5505d0d..13b8ca2858 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -58,4 +58,9 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
 SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
 SELECT pg_stat_statements_reset() IS NOT NULL AS t;
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/parallel.sql b/contrib/pg_stat_statements/sql/parallel.sql
new file mode 100644
index 0000000000..bdc9be6e1e
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/parallel.sql
@@ -0,0 +1,27 @@
+--
+-- Validate parallelization generation metrics
+--
+
+SET parallel_setup_cost TO 0;
+SET min_parallel_table_scan_size TO '100kB';
+SET pg_stat_statements.track_utility = FALSE;
+
+CREATE TABLE pgss_parallel_tab (a int);
+INSERT INTO pgss_parallel_tab SELECT generate_series(1, 10_000);
+SELECT count(*) FROM pgss_parallel_tab;
+DROP TABLE pgss_parallel_tab;
+
+-- Check parallelization metrics are generated for the SELECT statement
+SELECT query,
+  parallelized_queries_planned > 0 AS parallelized_queries_planned_generated,
+  parallelized_queries_launched > 0 AS parallelized_queries_launched_generated,
+  parallelized_workers_planned > 0 AS parallelized_workers_planned_generated,
+  parallelized_workers_launched > 0 AS parallelized_workers_launched_generated,
+  parallelized_nodes > 0 AS parallelized_nodes_generated,
+  parallelized_nodes_all_workers > 0 AS parallelized_nodes_all_workers_generated,
+  parallelized_nodes_no_worker = 0 AS parallelized_nodes_no_worker_generated 
+FROM pg_stat_statements
+WHERE query LIKE 'SELECT count%'
+ORDER BY query COLLATE "C";
+
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 9b0aff73b1..3fe84ba994 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -527,6 +527,69 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_queries_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement was planned to use parallelism.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_queries_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times that the statement was executed using parallelism
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_workers_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers planned for the query.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_workers_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers executed for the query.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes_all_workers</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes that got all workers
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_nodes_no_worker</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallelized nodes that got no worker at all
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5737f9f4eb..3b0715d604 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -158,10 +158,17 @@ CreateExecutorState(void)
 	estate->es_sourceText = NULL;
 
 	estate->es_use_parallel_mode = false;
+	estate->es_used_parallel_mode = false;
 
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
 
+	estate->es_parallelized_nodes = 0;
+	estate->es_parallelized_nodes_all_workers = 0;
+	estate->es_parallelized_nodes_no_worker = 0;
+	estate->es_parallelized_workers_launched = 0;
+	estate->es_parallelized_workers_planned = 0;
+
 	/*
 	 * Return the executor state structure
 	 */
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 5d4ffe989c..f1f772ecb6 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -181,7 +181,13 @@ ExecGather(PlanState *pstate)
 			LaunchParallelWorkers(pcxt);
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
-
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
+			estate->es_parallelized_nodes += 1;
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+			if (pcxt->nworkers_to_launch == pcxt->nworkers_launched)
+				estate->es_parallelized_nodes_all_workers += 1;
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
@@ -198,6 +204,7 @@ ExecGather(PlanState *pstate)
 				/* No workers?	Then never mind. */
 				node->nreaders = 0;
 				node->reader = NULL;
+				estate->es_parallelized_nodes_no_worker += 1;
 			}
 			node->nextreader = 0;
 		}
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 45f6017c29..5678b25a86 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -222,6 +222,13 @@ ExecGatherMerge(PlanState *pstate)
 			LaunchParallelWorkers(pcxt);
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
+			estate->es_parallelized_nodes += 1;
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+			if (pcxt->nworkers_to_launch == pcxt->nworkers_launched)
+				estate->es_parallelized_nodes_all_workers += 1;
 
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
@@ -239,6 +246,7 @@ ExecGatherMerge(PlanState *pstate)
 				/* No workers?	Then never mind. */
 				node->nreaders = 0;
 				node->reader = NULL;
+				estate->es_parallelized_nodes_no_worker += 1;
 			}
 		}
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index aab59d681c..d114e8d522 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -707,6 +707,12 @@ typedef struct EState
 	struct EPQState *es_epq_active;
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
+	bool		es_used_parallel_mode;	/* was executed in parallel */
+	int			es_parallelized_workers_launched;
+	int			es_parallelized_workers_planned;
+	int			es_parallelized_nodes; /* # of parallelized nodes */
+	int			es_parallelized_nodes_all_workers; /* # of nodes with all workers launched */
+	int			es_parallelized_nodes_no_worker; /* # of nodes with no workers launched */
 
 	/* The per-query shared memory area to use for parallel execution. */
 	struct dsa_area *es_query_dsa;
-- 
2.46.2

#4Michael Paquier
michael@paquier.xyz
In reply to: Guillaume Lelarge (#3)
Re: Add parallel columns for pg_stat_statements

On Sun, Oct 06, 2024 at 03:32:02PM +0200, Guillaume Lelarge wrote:

I'm not sure I follow. That would mean that every time a query is executed,
it always gets the same amount of workers. Which is not guaranteed to be
true.

I would agree, though, that parallelized_queries_launched is probably not
that interesting. I could get rid of it if you think it should go away.

My point is that these stats are useful to know which action may have
to be taken when reaching a mean, and numbers in pg_stat_statements
offer hints that something is going wrong and that a closer lookup at
an EXPLAIN plan may be required, particularly if the total number of
workers planned and launched aggregated in the counters is unbalanced
across queries. If the planned/launched ratio is balanced across most
queries queries, a GUC adjustment may be OK. If the ratio is very
unbalanced in a lower set of queries, I'd also look at tweaking GUCs
instead like the per_gather. These counters give information that one
or the other may be required.

Well, I don't see this as an overlap. Rather more information.

Later versions of Benoit's patch have been accumulating this data in
the executor state.  v4 posted at [1] has the following diffs:
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -724,6 +724,9 @@ typedef struct EState
 	 */
 	List	   *es_insert_pending_result_relations;
 	List	   *es_insert_pending_modifytables;
+
+	int			es_workers_launched;
+	int			es_workers_planned;
 } EState;

Your v2 posted on this thread has that:
@@ -707,6 +707,12 @@ typedef struct EState
struct EPQState *es_epq_active;

 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
+	bool		es_used_parallel_mode;	/* was executed in parallel */
+	int			es_parallelized_workers_launched;
+	int			es_parallelized_workers_planned;
+	int			es_parallelized_nodes; /* # of parallelized nodes */
+	int			es_parallelized_nodes_all_workers; /* # of nodes with all workers launched */
+	int			es_parallelized_nodes_no_worker; /* # of nodes with no workers launched */

es_parallelized_workers_launched and es_workers_launched are the same
thing in both.

On this, I would agree with you. They are not that particularly useful to
get better setting for parallel GUCs. I can drop them if you want.

Yep. I would remove them for now. This leads to more bloat.

Did this on the v2 version of the patch (attached here).

Thanks for your review. If you want the parallelized_queries_launched
column and the parallelized_nodes_* columns dropped, I can do that on a v3
patch.

I'd recommend to split that into more independent patches:
- Introduce the two counters in EState with the incrementations done
in nodeGatherMerge.c and nodeGather.c (mentioned that at [2]/messages/by-id/Zv46wTMjLTuu2t9J@paquier.xyz -- Michael, you may
want to coordinate with Benoit to avoid duplicating the work).
- Expand pg_stat_statements to use them for DMLs, SELECTs, well where
they matter.
- Look at expanding that for utilities that can do parallel jobs:
CREATE INDEX and VACUUM, but this has lower priority to me, and this
can reuse the same counters as the ones added by patch 2.

[1]: /messages/by-id/6ecad3ad-835c-486c-9ebd-da87a9a97634@dalibo.com
[2]: /messages/by-id/Zv46wTMjLTuu2t9J@paquier.xyz -- Michael
--
Michael

#5Guillaume Lelarge
guillaume@lelarge.info
In reply to: Michael Paquier (#4)
2 attachment(s)
Re: Add parallel columns for pg_stat_statements

Le lun. 7 oct. 2024 à 02:18, Michael Paquier <michael@paquier.xyz> a écrit :

On Sun, Oct 06, 2024 at 03:32:02PM +0200, Guillaume Lelarge wrote:

I'm not sure I follow. That would mean that every time a query is

executed,

it always gets the same amount of workers. Which is not guaranteed to be
true.

I would agree, though, that parallelized_queries_launched is probably not
that interesting. I could get rid of it if you think it should go away.

My point is that these stats are useful to know which action may have
to be taken when reaching a mean, and numbers in pg_stat_statements
offer hints that something is going wrong and that a closer lookup at
an EXPLAIN plan may be required, particularly if the total number of
workers planned and launched aggregated in the counters is unbalanced
across queries. If the planned/launched ratio is balanced across most
queries queries, a GUC adjustment may be OK. If the ratio is very
unbalanced in a lower set of queries, I'd also look at tweaking GUCs
instead like the per_gather. These counters give information that one
or the other may be required.

Well, I don't see this as an overlap. Rather more information.

Later versions of Benoit's patch have been accumulating this data in
the executor state.  v4 posted at [1] has the following diffs:
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -724,6 +724,9 @@ typedef struct EState
*/
List       *es_insert_pending_result_relations;
List       *es_insert_pending_modifytables;
+
+       int                     es_workers_launched;
+       int                     es_workers_planned;
} EState;

Your v2 posted on this thread has that:
@@ -707,6 +707,12 @@ typedef struct EState
struct EPQState *es_epq_active;

bool            es_use_parallel_mode;   /* can we use parallel
workers? */
+       bool            es_used_parallel_mode;  /* was executed in
parallel */
+       int                     es_parallelized_workers_launched;
+       int                     es_parallelized_workers_planned;
+       int                     es_parallelized_nodes; /* # of
parallelized nodes */
+       int                     es_parallelized_nodes_all_workers; /* # of
nodes with all workers launched */
+       int                     es_parallelized_nodes_no_worker; /* # of
nodes with no workers launched */

es_parallelized_workers_launched and es_workers_launched are the same
thing in both.

My bad. I agree this is the way to go. See patch v3-0001 attached.

On this, I would agree with you. They are not that particularly useful to
get better setting for parallel GUCs. I can drop them if you want.

Yep. I would remove them for now. This leads to more bloat.

Done. See patch v3-0002 attached.

Did this on the v2 version of the patch (attached here).

Thanks for your review. If you want the parallelized_queries_launched
column and the parallelized_nodes_* columns dropped, I can do that on a

v3

patch.

I'd recommend to split that into more independent patches:
- Introduce the two counters in EState with the incrementations done
in nodeGatherMerge.c and nodeGather.c (mentioned that at [2], you may
want to coordinate with Benoit to avoid duplicating the work).
- Expand pg_stat_statements to use them for DMLs, SELECTs, well where
they matter.
- Look at expanding that for utilities that can do parallel jobs:
CREATE INDEX and VACUUM, but this has lower priority to me, and this
can reuse the same counters as the ones added by patch 2.

The first two are done. The last one is beyond my scope.

I'm now working on Benoit's patch to make it work with my v3-0001 patch.
I'll send the resulting patch on his thread.

[1]:
/messages/by-id/6ecad3ad-835c-486c-9ebd-da87a9a97634@dalibo.com
[2]: /messages/by-id/Zv46wTMjLTuu2t9J@paquier.xyz
--
Michael

Regards.

--
Guillaume.

Attachments:

v3-0001-Introduce-two-new-counters-in-EState.patchtext/x-patch; charset=US-ASCII; name=v3-0001-Introduce-two-new-counters-in-EState.patchDownload
From 95b300eeff0168f2618418102df660f1ba9b9113 Mon Sep 17 00:00:00 2001
From: Guillaume Lelarge <guillaume.lelarge@dalibo.com>
Date: Mon, 7 Oct 2024 08:45:36 +0200
Subject: [PATCH v3 1/2] Introduce two new counters in EState

They will be used by two other patchs to populate new columns in
pg_stat_database and pg_statements.
---
 src/backend/executor/execUtils.c       | 3 +++
 src/backend/executor/nodeGather.c      | 3 +++
 src/backend/executor/nodeGatherMerge.c | 3 +++
 src/include/nodes/execnodes.h          | 3 +++
 4 files changed, 12 insertions(+)

diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5737f9f4eb..1908481999 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -162,6 +162,9 @@ CreateExecutorState(void)
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
 
+	estate->es_parallelized_workers_launched = 0;
+	estate->es_parallelized_workers_planned = 0;
+
 	/*
 	 * Return the executor state structure
 	 */
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 5d4ffe989c..0fb915175a 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -182,6 +182,9 @@ ExecGather(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 45f6017c29..149ab23d90 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -223,6 +223,9 @@ ExecGatherMerge(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
+			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
+
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index aab59d681c..f898590ece 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -708,6 +708,9 @@ typedef struct EState
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
 
+	int			es_parallelized_workers_launched;
+	int			es_parallelized_workers_planned;
+
 	/* The per-query shared memory area to use for parallel execution. */
 	struct dsa_area *es_query_dsa;
 
-- 
2.46.2

v3-0002-Add-parallel-columns-to-pg_stat_statements.patchtext/x-patch; charset=US-ASCII; name=v3-0002-Add-parallel-columns-to-pg_stat_statements.patchDownload
From bf882530dbbc4423a24ae7f9a0bd22f674664832 Mon Sep 17 00:00:00 2001
From: Guillaume Lelarge <guillaume.lelarge@dalibo.com>
Date: Mon, 7 Oct 2024 09:47:09 +0200
Subject: [PATCH v3 2/2] Add parallel columns to pg_stat_statements

There are four new columns:
 * parallelized_queries_planned (number of times the query has been planned to
   be parallelized),
 * parallel_ized_querieslaunched (number of times the query has been executed
   with parallelization),
 * parallelized_workers_planned (number of parallel workers planned for
   this query),
 * parallelized_workers_launched (number of parallel workers executed for
   this query).

These new columns will help to monitor and better configure query
parallelization.
---
 contrib/pg_stat_statements/Makefile           |  4 +-
 .../expected/oldextversions.out               | 66 +++++++++++++++
 .../pg_stat_statements/expected/parallel.out  | 35 ++++++++
 .../pg_stat_statements--1.11--1.12.sql        | 77 ++++++++++++++++++
 .../pg_stat_statements/pg_stat_statements.c   | 81 +++++++++++++++++--
 .../pg_stat_statements.control                |  2 +-
 .../pg_stat_statements/sql/oldextversions.sql |  5 ++
 contrib/pg_stat_statements/sql/parallel.sql   | 24 ++++++
 doc/src/sgml/pgstatstatements.sgml            | 36 +++++++++
 src/backend/executor/execUtils.c              |  1 +
 src/backend/executor/nodeGather.c             |  2 +
 src/backend/executor/nodeGatherMerge.c        |  2 +
 src/include/nodes/execnodes.h                 |  1 +
 13 files changed, 326 insertions(+), 10 deletions(-)
 create mode 100644 contrib/pg_stat_statements/expected/parallel.out
 create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
 create mode 100644 contrib/pg_stat_statements/sql/parallel.sql

diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 1622b43ded..0caeea0c03 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -7,7 +7,7 @@ OBJS = \
 
 EXTENSION = pg_stat_statements
 DATA = pg_stat_statements--1.4.sql \
-	pg_stat_statements--1.10--1.11.sql \
+	pg_stat_statements--1.11--1.12.sql pg_stat_statements--1.10--1.11.sql \
 	pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
 	pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
 	pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -19,7 +19,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
 
 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
 REGRESS = select dml cursors utility level_tracking planning \
-	user_activity wal entry_timestamp privileges extended cleanup \
+	user_activity wal entry_timestamp privileges extended parallel cleanup \
 	oldextversions
 # Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
 # which typical installcheck users do not have (e.g. buildfarm clients).
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5842c930e5..1937224f56 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -342,4 +342,70 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
  t
 (1 row)
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+                             View "public.pg_stat_statements"
+            Column             |           Type           | Collation | Nullable | Default 
+-------------------------------+--------------------------+-----------+----------+---------
+ userid                        | oid                      |           |          | 
+ dbid                          | oid                      |           |          | 
+ toplevel                      | boolean                  |           |          | 
+ queryid                       | bigint                   |           |          | 
+ query                         | text                     |           |          | 
+ plans                         | bigint                   |           |          | 
+ total_plan_time               | double precision         |           |          | 
+ min_plan_time                 | double precision         |           |          | 
+ max_plan_time                 | double precision         |           |          | 
+ mean_plan_time                | double precision         |           |          | 
+ stddev_plan_time              | double precision         |           |          | 
+ calls                         | bigint                   |           |          | 
+ total_exec_time               | double precision         |           |          | 
+ min_exec_time                 | double precision         |           |          | 
+ max_exec_time                 | double precision         |           |          | 
+ mean_exec_time                | double precision         |           |          | 
+ stddev_exec_time              | double precision         |           |          | 
+ rows                          | bigint                   |           |          | 
+ shared_blks_hit               | bigint                   |           |          | 
+ shared_blks_read              | bigint                   |           |          | 
+ shared_blks_dirtied           | bigint                   |           |          | 
+ shared_blks_written           | bigint                   |           |          | 
+ local_blks_hit                | bigint                   |           |          | 
+ local_blks_read               | bigint                   |           |          | 
+ local_blks_dirtied            | bigint                   |           |          | 
+ local_blks_written            | bigint                   |           |          | 
+ temp_blks_read                | bigint                   |           |          | 
+ temp_blks_written             | bigint                   |           |          | 
+ shared_blk_read_time          | double precision         |           |          | 
+ shared_blk_write_time         | double precision         |           |          | 
+ local_blk_read_time           | double precision         |           |          | 
+ local_blk_write_time          | double precision         |           |          | 
+ temp_blk_read_time            | double precision         |           |          | 
+ temp_blk_write_time           | double precision         |           |          | 
+ wal_records                   | bigint                   |           |          | 
+ wal_fpi                       | bigint                   |           |          | 
+ wal_bytes                     | numeric                  |           |          | 
+ jit_functions                 | bigint                   |           |          | 
+ jit_generation_time           | double precision         |           |          | 
+ jit_inlining_count            | bigint                   |           |          | 
+ jit_inlining_time             | double precision         |           |          | 
+ jit_optimization_count        | bigint                   |           |          | 
+ jit_optimization_time         | double precision         |           |          | 
+ jit_emission_count            | bigint                   |           |          | 
+ jit_emission_time             | double precision         |           |          | 
+ jit_deform_count              | bigint                   |           |          | 
+ jit_deform_time               | double precision         |           |          | 
+ parallelized_queries_planned  | bigint                   |           |          | 
+ parallelized_queries_launched | bigint                   |           |          | 
+ parallelized_workers_planned  | bigint                   |           |          | 
+ parallelized_workers_launched | bigint                   |           |          | 
+ stats_since                   | timestamp with time zone |           |          | 
+ minmax_stats_since            | timestamp with time zone |           |          | 
+
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+ has_data 
+----------
+ t
+(1 row)
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/parallel.out b/contrib/pg_stat_statements/expected/parallel.out
new file mode 100644
index 0000000000..6e7c8ffb46
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/parallel.out
@@ -0,0 +1,35 @@
+--
+-- Validate parallelization generation metrics
+--
+SET parallel_setup_cost TO 0;
+SET min_parallel_table_scan_size TO '100kB';
+SET pg_stat_statements.track_utility = FALSE;
+CREATE TABLE pgss_parallel_tab (a int);
+INSERT INTO pgss_parallel_tab SELECT generate_series(1, 10_000);
+SELECT count(*) FROM pgss_parallel_tab;
+ count 
+-------
+ 10000
+(1 row)
+
+DROP TABLE pgss_parallel_tab;
+-- Check parallelization metrics are generated for the SELECT statement
+SELECT query,
+  parallelized_queries_planned > 0 AS parallelized_queries_planned_generated,
+  parallelized_queries_launched > 0 AS parallelized_queries_launched_generated,
+  parallelized_workers_planned > 0 AS parallelized_workers_planned_generated,
+  parallelized_workers_launched > 0 AS parallelized_workers_launched_generated
+FROM pg_stat_statements
+WHERE query LIKE 'SELECT count%'
+ORDER BY query COLLATE "C";
+                 query                  | parallelized_queries_planned_generated | parallelized_queries_launched_generated | parallelized_workers_planned_generated | parallelized_workers_launched_generated 
+----------------------------------------+----------------------------------------+-----------------------------------------+----------------------------------------+-----------------------------------------
+ SELECT count(*) FROM pgss_parallel_tab | t                                      | t                                       | t                                      | t
+(1 row)
+
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
new file mode 100644
index 0000000000..fe6e81eb0f
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
@@ -0,0 +1,77 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.12'" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements;
+ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements(boolean);
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+    OUT userid oid,
+    OUT dbid oid,
+    OUT toplevel bool,
+    OUT queryid bigint,
+    OUT query text,
+    OUT plans int8,
+    OUT total_plan_time float8,
+    OUT min_plan_time float8,
+    OUT max_plan_time float8,
+    OUT mean_plan_time float8,
+    OUT stddev_plan_time float8,
+    OUT calls int8,
+    OUT total_exec_time float8,
+    OUT min_exec_time float8,
+    OUT max_exec_time float8,
+    OUT mean_exec_time float8,
+    OUT stddev_exec_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8,
+    OUT shared_blk_read_time float8,
+    OUT shared_blk_write_time float8,
+    OUT local_blk_read_time float8,
+    OUT local_blk_write_time float8,
+    OUT temp_blk_read_time float8,
+    OUT temp_blk_write_time float8,
+    OUT wal_records int8,
+    OUT wal_fpi int8,
+    OUT wal_bytes numeric,
+    OUT jit_functions int8,
+    OUT jit_generation_time float8,
+    OUT jit_inlining_count int8,
+    OUT jit_inlining_time float8,
+    OUT jit_optimization_count int8,
+    OUT jit_optimization_time float8,
+    OUT jit_emission_count int8,
+    OUT jit_emission_time float8,
+    OUT jit_deform_count int8,
+    OUT jit_deform_time float8,
+    OUT parallelized_queries_planned int8,
+    OUT parallelized_queries_launched int8,
+    OUT parallelized_workers_planned int8,
+    OUT parallelized_workers_launched int8,
+    OUT stats_since timestamp with time zone,
+    OUT minmax_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_12'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5765ef49b4..939a27e829 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -113,6 +113,7 @@ typedef enum pgssVersion
 	PGSS_V1_9,
 	PGSS_V1_10,
 	PGSS_V1_11,
+	PGSS_V1_12,
 } pgssVersion;
 
 typedef enum pgssStoreKind
@@ -204,6 +205,10 @@ typedef struct Counters
 	int64		jit_emission_count; /* number of times emission time has been
 									 * > 0 */
 	double		jit_emission_time;	/* total time to emit jit code */
+	int64       parallelized_queries_planned;	/* # of times query was planned to use parallelism */
+	int64       parallelized_queries_launched;	/* # of times query was executed using parallelism */
+	int64       parallelized_workers_planned;	/* # of parallel workers planned */
+	int64       parallelized_workers_launched;	/* # of parallel workers launched */
 } Counters;
 
 /*
@@ -317,6 +322,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
 PG_FUNCTION_INFO_V1(pg_stat_statements);
 PG_FUNCTION_INFO_V1(pg_stat_statements_info);
 
@@ -347,7 +353,11 @@ static void pgss_store(const char *query, uint64 queryId,
 					   const BufferUsage *bufusage,
 					   const WalUsage *walusage,
 					   const struct JitInstrumentation *jitusage,
-					   JumbleState *jstate);
+					   JumbleState *jstate,
+					   bool parallelized_queries_planned,
+					   bool parallelized_queries_launched,
+					   int parallelized_workers_planned,
+					   int parallelized_workers_launched);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 										pgssVersion api_version,
 										bool showtext);
@@ -867,7 +877,11 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
 				   NULL,
 				   NULL,
 				   NULL,
-				   jstate);
+				   jstate,
+				   false,
+				   false,
+				   0,
+				   0);
 }
 
 /*
@@ -945,7 +959,11 @@ pgss_planner(Query *parse,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1078,7 +1096,11 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   &queryDesc->totaltime->bufusage,
 				   &queryDesc->totaltime->walusage,
 				   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
-				   NULL);
+				   NULL,
+				   queryDesc->plannedstmt->parallelModeNeeded,
+				   queryDesc->estate->es_used_parallel_mode,
+				   queryDesc->estate->es_parallelized_workers_planned,
+				   queryDesc->estate->es_parallelized_workers_launched);
 	}
 
 	if (prev_ExecutorEnd)
@@ -1209,7 +1231,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   false,
+				   false,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1270,7 +1296,11 @@ pgss_store(const char *query, uint64 queryId,
 		   const BufferUsage *bufusage,
 		   const WalUsage *walusage,
 		   const struct JitInstrumentation *jitusage,
-		   JumbleState *jstate)
+		   JumbleState *jstate,
+		   bool parallelized_queries_planned,
+		   bool parallelized_queries_launched,
+		   int parallelized_workers_planned,
+		   int parallelized_workers_launched)
 {
 	pgssHashKey key;
 	pgssEntry  *entry;
@@ -1472,6 +1502,17 @@ pgss_store(const char *query, uint64 queryId,
 				entry->counters.jit_emission_count++;
 			entry->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
 		}
+		/* inc parallel counters */
+		if (parallelized_queries_planned)
+		{
+			entry->counters.parallelized_queries_planned += 1;
+		}
+		if (parallelized_queries_launched)
+		{
+			entry->counters.parallelized_queries_launched += 1;
+		}
+		entry->counters.parallelized_workers_planned += parallelized_workers_planned;
+		entry->counters.parallelized_workers_launched += parallelized_workers_launched;
 
 		SpinLockRelease(&entry->mutex);
 	}
@@ -1539,7 +1580,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_9	33
 #define PG_STAT_STATEMENTS_COLS_V1_10	43
 #define PG_STAT_STATEMENTS_COLS_V1_11	49
-#define PG_STAT_STATEMENTS_COLS			49	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_12	53
+#define PG_STAT_STATEMENTS_COLS			53	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1551,6 +1593,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
  * expected API version is identified by embedding it in the C name of the
  * function.  Unfortunately we weren't bright enough to do that for 1.1.
  */
+Datum
+pg_stat_statements_1_12(PG_FUNCTION_ARGS)
+{
+	bool		showtext = PG_GETARG_BOOL(0);
+
+	pg_stat_statements_internal(fcinfo, PGSS_V1_12, showtext);
+
+	return (Datum) 0;
+}
+
 Datum
 pg_stat_statements_1_11(PG_FUNCTION_ARGS)
 {
@@ -1695,6 +1747,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 			if (api_version != PGSS_V1_11)
 				elog(ERROR, "incorrect number of output arguments");
 			break;
+		case PG_STAT_STATEMENTS_COLS_V1_12:
+			if (api_version != PGSS_V1_12)
+				elog(ERROR, "incorrect number of output arguments");
+			break;
 		default:
 			elog(ERROR, "incorrect number of output arguments");
 	}
@@ -1932,6 +1988,16 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 		{
 			values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
 			values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
+		}
+		if (api_version >= PGSS_V1_12)
+		{
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_queries_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallelized_workers_launched);
+		}
+		if (api_version >= PGSS_V1_11)
+		{
 			values[i++] = TimestampTzGetDatum(stats_since);
 			values[i++] = TimestampTzGetDatum(minmax_stats_since);
 		}
@@ -1944,6 +2010,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 					 api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
 					 api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
 					 api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
+					 api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
 					 -1 /* fail if you forget to update this assert */ ));
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 8a76106ec6..d45ebc12e3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.11'
+default_version = '1.12'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 38d5505d0d..13b8ca2858 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -58,4 +58,9 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
 SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
 SELECT pg_stat_statements_reset() IS NOT NULL AS t;
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/parallel.sql b/contrib/pg_stat_statements/sql/parallel.sql
new file mode 100644
index 0000000000..8b32edffb5
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/parallel.sql
@@ -0,0 +1,24 @@
+--
+-- Validate parallelization generation metrics
+--
+
+SET parallel_setup_cost TO 0;
+SET min_parallel_table_scan_size TO '100kB';
+SET pg_stat_statements.track_utility = FALSE;
+
+CREATE TABLE pgss_parallel_tab (a int);
+INSERT INTO pgss_parallel_tab SELECT generate_series(1, 10_000);
+SELECT count(*) FROM pgss_parallel_tab;
+DROP TABLE pgss_parallel_tab;
+
+-- Check parallelization metrics are generated for the SELECT statement
+SELECT query,
+  parallelized_queries_planned > 0 AS parallelized_queries_planned_generated,
+  parallelized_queries_launched > 0 AS parallelized_queries_launched_generated,
+  parallelized_workers_planned > 0 AS parallelized_workers_planned_generated,
+  parallelized_workers_launched > 0 AS parallelized_workers_launched_generated
+FROM pg_stat_statements
+WHERE query LIKE 'SELECT count%'
+ORDER BY query COLLATE "C";
+
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 9b0aff73b1..ce66fe1b16 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -527,6 +527,42 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_queries_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement was planned to use parallelism.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_queries_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times that the statement was executed using parallelism
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallelized_workers_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers planned for the query.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_workers_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers executed for the query.
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 1908481999..634ba5caed 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -158,6 +158,7 @@ CreateExecutorState(void)
 	estate->es_sourceText = NULL;
 
 	estate->es_use_parallel_mode = false;
+	estate->es_used_parallel_mode = false;
 
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 0fb915175a..489bfbfe87 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -182,6 +182,8 @@ ExecGather(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
 			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
 			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
 
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 149ab23d90..df0a36d3ac 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -223,6 +223,8 @@ ExecGatherMerge(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			if(pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
 			estate->es_parallelized_workers_launched += pcxt->nworkers_launched;
 			estate->es_parallelized_workers_planned += pcxt->nworkers_to_launch;
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f898590ece..9e67de4a4d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -708,6 +708,7 @@ typedef struct EState
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
 
+	bool		es_used_parallel_mode;	/* was executed in parallel */
 	int			es_parallelized_workers_launched;
 	int			es_parallelized_workers_planned;
 
-- 
2.46.2

#6Michael Paquier
michael@paquier.xyz
In reply to: Guillaume Lelarge (#5)
3 attachment(s)
Re: Add parallel columns for pg_stat_statements

On Mon, Oct 07, 2024 at 10:00:13AM +0200, Guillaume Lelarge wrote:

Le lun. 7 oct. 2024 à 02:18, Michael Paquier <michael@paquier.xyz> a écrit :

I'd recommend to split that into more independent patches:
- Introduce the two counters in EState with the incrementations done
in nodeGatherMerge.c and nodeGather.c (mentioned that at [2], you may
want to coordinate with Benoit to avoid duplicating the work).
- Expand pg_stat_statements to use them for DMLs, SELECTs, well where
they matter.
- Look at expanding that for utilities that can do parallel jobs:
CREATE INDEX and VACUUM, but this has lower priority to me, and this
can reuse the same counters as the ones added by patch 2.

The first two are done. The last one is beyond my scope.

That's fair. I have put my hands on this patch set, finishing with
the attached.

A couple of notes:
- I've been struggling a bit on the "planned" vs "launched" terms used
in the names for the counters. It is inconsistent with the backend
state, where we talk about workers "to launch" and workers "launched".
"planned" does not really apply to utilities, as this may not be
planned per se.
- The test in parallel.sql can be cheaper, tweaking the right GUCs the
right way data in the table is not even required to spawn a set of
parallel workers.
- Meson was not updated for the new test and the files to install.

0001 and 0002 are the parts of the patch that I can see myself
applying; it is pretty cool to see pg_stat_statements complain that
the launched/to_launch ratio can get unbalanced really quickly when I
do something stupid. The CI is stable with these.

0003 has the remaining bits with the 3rd and 4th counters, able to
apply on top of 0002.
--
Michael

Attachments:

v4-0001-Introduce-two-new-counters-in-EState-for-parallel.patchtext/x-diff; charset=us-asciiDownload
From 5dc567cda5ad9e0c41532ef6ce5cc5f1808e974e Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 8 Oct 2024 15:52:38 +0900
Subject: [PATCH v4 1/3] Introduce two new counters in EState for parallel
 workers

They will be used by some follow-up patches to populate other parts of
the system with this data extracted from the executor.
---
 src/include/nodes/execnodes.h          | 5 +++++
 src/backend/executor/execUtils.c       | 2 ++
 src/backend/executor/nodeGather.c      | 7 +++++++
 src/backend/executor/nodeGatherMerge.c | 7 +++++++
 4 files changed, 21 insertions(+)

diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index aab59d681c..e4698a28c4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -708,6 +708,11 @@ typedef struct EState
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
 
+	int			es_parallel_workers_to_launch;	/* number of workers to
+												 * launch. */
+	int			es_parallel_workers_launched;	/* number of workers actually
+												 * launched. */
+
 	/* The per-query shared memory area to use for parallel execution. */
 	struct dsa_area *es_query_dsa;
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5737f9f4eb..6712302ec8 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -158,6 +158,8 @@ CreateExecutorState(void)
 	estate->es_sourceText = NULL;
 
 	estate->es_use_parallel_mode = false;
+	estate->es_parallel_workers_to_launch = 0;
+	estate->es_parallel_workers_launched = 0;
 
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 5d4ffe989c..7f7edc7f9f 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -182,6 +182,13 @@ ExecGather(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			/*
+			 * Count number of workers originally wanted and actually
+			 * launched.
+			 */
+			estate->es_parallel_workers_to_launch += pcxt->nworkers_to_launch;
+			estate->es_parallel_workers_launched += pcxt->nworkers_launched;
+
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 45f6017c29..bc99c0b448 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -223,6 +223,13 @@ ExecGatherMerge(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			/*
+			 * Count number of workers originally wanted and actually
+			 * launched.
+			 */
+			estate->es_parallel_workers_to_launch += pcxt->nworkers_to_launch;
+			estate->es_parallel_workers_launched += pcxt->nworkers_launched;
+
 			/* Set up tuple queue readers to read the results. */
 			if (pcxt->nworkers_launched > 0)
 			{
-- 
2.45.2

v4-0002-Add-parallel-columns-to-pg_stat_statements.patchtext/x-diff; charset=us-asciiDownload
From a28475ff5b1e17ee7295a3daabc79111c30b96d3 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 8 Oct 2024 15:56:22 +0900
Subject: [PATCH v4 2/3] Add parallel columns to pg_stat_statements

---
 doc/src/sgml/pgstatstatements.sgml            | 18 +++++
 contrib/pg_stat_statements/Makefile           |  6 +-
 .../expected/oldextversions.out               | 64 ++++++++++++++++
 .../pg_stat_statements/expected/parallel.out  | 34 +++++++++
 contrib/pg_stat_statements/meson.build        |  2 +
 .../pg_stat_statements--1.11--1.12.sql        | 75 +++++++++++++++++++
 .../pg_stat_statements/pg_stat_statements.c   | 60 +++++++++++++--
 .../pg_stat_statements.control                |  2 +-
 .../pg_stat_statements/sql/oldextversions.sql |  5 ++
 contrib/pg_stat_statements/sql/parallel.sql   | 26 +++++++
 10 files changed, 281 insertions(+), 11 deletions(-)
 create mode 100644 contrib/pg_stat_statements/expected/parallel.out
 create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
 create mode 100644 contrib/pg_stat_statements/sql/parallel.sql

diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 9b0aff73b1..501b468e9a 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -527,6 +527,24 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_workers_to_launch</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers planned to be launched
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_workers_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of parallel workers actually launched
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 1622b43ded..241c02587b 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -7,7 +7,7 @@ OBJS = \
 
 EXTENSION = pg_stat_statements
 DATA = pg_stat_statements--1.4.sql \
-	pg_stat_statements--1.10--1.11.sql \
+	pg_stat_statements--1.11--1.12.sql pg_stat_statements--1.10--1.11.sql \
 	pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
 	pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
 	pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -19,8 +19,8 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
 
 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
 REGRESS = select dml cursors utility level_tracking planning \
-	user_activity wal entry_timestamp privileges extended cleanup \
-	oldextversions
+	user_activity wal entry_timestamp privileges extended \
+	parallel cleanup oldextversions
 # Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
 # which typical installcheck users do not have (e.g. buildfarm clients).
 NO_INSTALLCHECK = 1
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5842c930e5..0c60fc8127 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -342,4 +342,68 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
  t
 (1 row)
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+                            View "public.pg_stat_statements"
+           Column           |           Type           | Collation | Nullable | Default 
+----------------------------+--------------------------+-----------+----------+---------
+ userid                     | oid                      |           |          | 
+ dbid                       | oid                      |           |          | 
+ toplevel                   | boolean                  |           |          | 
+ queryid                    | bigint                   |           |          | 
+ query                      | text                     |           |          | 
+ plans                      | bigint                   |           |          | 
+ total_plan_time            | double precision         |           |          | 
+ min_plan_time              | double precision         |           |          | 
+ max_plan_time              | double precision         |           |          | 
+ mean_plan_time             | double precision         |           |          | 
+ stddev_plan_time           | double precision         |           |          | 
+ calls                      | bigint                   |           |          | 
+ total_exec_time            | double precision         |           |          | 
+ min_exec_time              | double precision         |           |          | 
+ max_exec_time              | double precision         |           |          | 
+ mean_exec_time             | double precision         |           |          | 
+ stddev_exec_time           | double precision         |           |          | 
+ rows                       | bigint                   |           |          | 
+ shared_blks_hit            | bigint                   |           |          | 
+ shared_blks_read           | bigint                   |           |          | 
+ shared_blks_dirtied        | bigint                   |           |          | 
+ shared_blks_written        | bigint                   |           |          | 
+ local_blks_hit             | bigint                   |           |          | 
+ local_blks_read            | bigint                   |           |          | 
+ local_blks_dirtied         | bigint                   |           |          | 
+ local_blks_written         | bigint                   |           |          | 
+ temp_blks_read             | bigint                   |           |          | 
+ temp_blks_written          | bigint                   |           |          | 
+ shared_blk_read_time       | double precision         |           |          | 
+ shared_blk_write_time      | double precision         |           |          | 
+ local_blk_read_time        | double precision         |           |          | 
+ local_blk_write_time       | double precision         |           |          | 
+ temp_blk_read_time         | double precision         |           |          | 
+ temp_blk_write_time        | double precision         |           |          | 
+ wal_records                | bigint                   |           |          | 
+ wal_fpi                    | bigint                   |           |          | 
+ wal_bytes                  | numeric                  |           |          | 
+ jit_functions              | bigint                   |           |          | 
+ jit_generation_time        | double precision         |           |          | 
+ jit_inlining_count         | bigint                   |           |          | 
+ jit_inlining_time          | double precision         |           |          | 
+ jit_optimization_count     | bigint                   |           |          | 
+ jit_optimization_time      | double precision         |           |          | 
+ jit_emission_count         | bigint                   |           |          | 
+ jit_emission_time          | double precision         |           |          | 
+ jit_deform_count           | bigint                   |           |          | 
+ jit_deform_time            | double precision         |           |          | 
+ parallel_workers_to_launch | bigint                   |           |          | 
+ parallel_workers_launched  | bigint                   |           |          | 
+ stats_since                | timestamp with time zone |           |          | 
+ minmax_stats_since         | timestamp with time zone |           |          | 
+
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+ has_data 
+----------
+ t
+(1 row)
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/parallel.out b/contrib/pg_stat_statements/expected/parallel.out
new file mode 100644
index 0000000000..8af3bd2c91
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/parallel.out
@@ -0,0 +1,34 @@
+--
+-- Tests for parallel statistics
+--
+SET pg_stat_statements.track_utility = FALSE;
+-- encourage use of parallel plans
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers_per_gather = 2;
+CREATE TABLE pgss_parallel_tab (a int);
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+SELECT count(*) FROM pgss_parallel_tab;
+ count 
+-------
+     0
+(1 row)
+
+SELECT query,
+  parallel_workers_to_launch > 0 AS has_workers_to_launch,
+  parallel_workers_launched > 0 AS has_workers_launched
+  FROM pg_stat_statements
+  WHERE query ~ 'SELECT count'
+  ORDER BY query COLLATE "C";
+                 query                  | has_workers_to_launch | has_workers_launched 
+----------------------------------------+-----------------------+----------------------
+ SELECT count(*) FROM pgss_parallel_tab | t                     | t
+(1 row)
+
+DROP TABLE pgss_parallel_tab;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index e14669ca15..e659b5e2b7 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
 install_data(
   'pg_stat_statements.control',
   'pg_stat_statements--1.4.sql',
+  'pg_stat_statements--1.11--1.12.sql',
   'pg_stat_statements--1.10--1.11.sql',
   'pg_stat_statements--1.9--1.10.sql',
   'pg_stat_statements--1.8--1.9.sql',
@@ -52,6 +53,7 @@ tests += {
       'entry_timestamp',
       'privileges',
       'extended',
+      'parallel',
       'cleanup',
       'oldextversions',
     ],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
new file mode 100644
index 0000000000..80e6be2544
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
@@ -0,0 +1,75 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.12'" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements;
+ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements(boolean);
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+    OUT userid oid,
+    OUT dbid oid,
+    OUT toplevel bool,
+    OUT queryid bigint,
+    OUT query text,
+    OUT plans int8,
+    OUT total_plan_time float8,
+    OUT min_plan_time float8,
+    OUT max_plan_time float8,
+    OUT mean_plan_time float8,
+    OUT stddev_plan_time float8,
+    OUT calls int8,
+    OUT total_exec_time float8,
+    OUT min_exec_time float8,
+    OUT max_exec_time float8,
+    OUT mean_exec_time float8,
+    OUT stddev_exec_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8,
+    OUT shared_blk_read_time float8,
+    OUT shared_blk_write_time float8,
+    OUT local_blk_read_time float8,
+    OUT local_blk_write_time float8,
+    OUT temp_blk_read_time float8,
+    OUT temp_blk_write_time float8,
+    OUT wal_records int8,
+    OUT wal_fpi int8,
+    OUT wal_bytes numeric,
+    OUT jit_functions int8,
+    OUT jit_generation_time float8,
+    OUT jit_inlining_count int8,
+    OUT jit_inlining_time float8,
+    OUT jit_optimization_count int8,
+    OUT jit_optimization_time float8,
+    OUT jit_emission_count int8,
+    OUT jit_emission_time float8,
+    OUT jit_deform_count int8,
+    OUT jit_deform_time float8,
+    OUT parallel_workers_to_launch int8,
+    OUT parallel_workers_launched int8,
+    OUT stats_since timestamp with time zone,
+    OUT minmax_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_12'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5765ef49b4..773b821911 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -113,6 +113,7 @@ typedef enum pgssVersion
 	PGSS_V1_9,
 	PGSS_V1_10,
 	PGSS_V1_11,
+	PGSS_V1_12,
 } pgssVersion;
 
 typedef enum pgssStoreKind
@@ -204,6 +205,10 @@ typedef struct Counters
 	int64		jit_emission_count; /* number of times emission time has been
 									 * > 0 */
 	double		jit_emission_time;	/* total time to emit jit code */
+	int64       parallel_workers_to_launch;	/* # of parallel workers planned to
+											 * be launched */
+	int64       parallel_workers_launched;	/* # of parallel workers actually
+											 * launched */
 } Counters;
 
 /*
@@ -317,6 +322,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
 PG_FUNCTION_INFO_V1(pg_stat_statements);
 PG_FUNCTION_INFO_V1(pg_stat_statements_info);
 
@@ -347,7 +353,9 @@ static void pgss_store(const char *query, uint64 queryId,
 					   const BufferUsage *bufusage,
 					   const WalUsage *walusage,
 					   const struct JitInstrumentation *jitusage,
-					   JumbleState *jstate);
+					   JumbleState *jstate,
+					   int parallel_workers_to_launch,
+					   int parallel_workers_launched);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 										pgssVersion api_version,
 										bool showtext);
@@ -867,7 +875,9 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
 				   NULL,
 				   NULL,
 				   NULL,
-				   jstate);
+				   jstate,
+				   0,
+				   0);
 }
 
 /*
@@ -945,7 +955,9 @@ pgss_planner(Query *parse,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1078,7 +1090,9 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   &queryDesc->totaltime->bufusage,
 				   &queryDesc->totaltime->walusage,
 				   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
-				   NULL);
+				   NULL,
+				   queryDesc->estate->es_parallel_workers_to_launch,
+				   queryDesc->estate->es_parallel_workers_launched);
 	}
 
 	if (prev_ExecutorEnd)
@@ -1209,7 +1223,9 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				   &bufusage,
 				   &walusage,
 				   NULL,
-				   NULL);
+				   NULL,
+				   0,
+				   0);
 	}
 	else
 	{
@@ -1270,7 +1286,9 @@ pgss_store(const char *query, uint64 queryId,
 		   const BufferUsage *bufusage,
 		   const WalUsage *walusage,
 		   const struct JitInstrumentation *jitusage,
-		   JumbleState *jstate)
+		   JumbleState *jstate,
+		   int parallel_workers_to_launch,
+		   int parallel_workers_launched)
 {
 	pgssHashKey key;
 	pgssEntry  *entry;
@@ -1473,6 +1491,10 @@ pgss_store(const char *query, uint64 queryId,
 			entry->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
 		}
 
+		/* parallel worker counters */
+		entry->counters.parallel_workers_to_launch += parallel_workers_to_launch;
+		entry->counters.parallel_workers_launched += parallel_workers_launched;
+
 		SpinLockRelease(&entry->mutex);
 	}
 
@@ -1539,7 +1561,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_9	33
 #define PG_STAT_STATEMENTS_COLS_V1_10	43
 #define PG_STAT_STATEMENTS_COLS_V1_11	49
-#define PG_STAT_STATEMENTS_COLS			49	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_12	51
+#define PG_STAT_STATEMENTS_COLS			51	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1551,6 +1574,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
  * expected API version is identified by embedding it in the C name of the
  * function.  Unfortunately we weren't bright enough to do that for 1.1.
  */
+Datum
+pg_stat_statements_1_12(PG_FUNCTION_ARGS)
+{
+	bool		showtext = PG_GETARG_BOOL(0);
+
+	pg_stat_statements_internal(fcinfo, PGSS_V1_12, showtext);
+
+	return (Datum) 0;
+}
+
 Datum
 pg_stat_statements_1_11(PG_FUNCTION_ARGS)
 {
@@ -1695,6 +1728,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 			if (api_version != PGSS_V1_11)
 				elog(ERROR, "incorrect number of output arguments");
 			break;
+		case PG_STAT_STATEMENTS_COLS_V1_12:
+			if (api_version != PGSS_V1_12)
+				elog(ERROR, "incorrect number of output arguments");
+			break;
 		default:
 			elog(ERROR, "incorrect number of output arguments");
 	}
@@ -1932,6 +1969,14 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 		{
 			values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
 			values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
+		}
+		if (api_version >= PGSS_V1_12)
+		{
+			values[i++] = Int64GetDatumFast(tmp.parallel_workers_to_launch);
+			values[i++] = Int64GetDatumFast(tmp.parallel_workers_launched);
+		}
+		if (api_version >= PGSS_V1_11)
+		{
 			values[i++] = TimestampTzGetDatum(stats_since);
 			values[i++] = TimestampTzGetDatum(minmax_stats_since);
 		}
@@ -1944,6 +1989,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 					 api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
 					 api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
 					 api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
+					 api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
 					 -1 /* fail if you forget to update this assert */ ));
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 8a76106ec6..d45ebc12e3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.11'
+default_version = '1.12'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 38d5505d0d..13b8ca2858 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -58,4 +58,9 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
 SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
 SELECT pg_stat_statements_reset() IS NOT NULL AS t;
 
+-- New functions and views for pg_stat_statements in 1.12
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
+\d pg_stat_statements
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/parallel.sql b/contrib/pg_stat_statements/sql/parallel.sql
new file mode 100644
index 0000000000..4ce1573d13
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/parallel.sql
@@ -0,0 +1,26 @@
+--
+-- Tests for parallel statistics
+--
+
+SET pg_stat_statements.track_utility = FALSE;
+
+-- encourage use of parallel plans
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers_per_gather = 2;
+
+CREATE TABLE pgss_parallel_tab (a int);
+
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+
+SELECT count(*) FROM pgss_parallel_tab;
+
+SELECT query,
+  parallel_workers_to_launch > 0 AS has_workers_to_launch,
+  parallel_workers_launched > 0 AS has_workers_launched
+  FROM pg_stat_statements
+  WHERE query ~ 'SELECT count'
+  ORDER BY query COLLATE "C";
+
+DROP TABLE pgss_parallel_tab;
-- 
2.45.2

v4-0003-Introduce-two-more-counters-in-pg_stat_statements.patchtext/x-diff; charset=us-asciiDownload
From c01361e32cbc4bc82a88c0568fa93a1a1565032b Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 8 Oct 2024 16:26:13 +0900
Subject: [PATCH v4 3/3] Introduce two more counters in pg_stat_statements

These track if a query has used parallelism at execution time, and if
parallelism was actually initially planned or not, incrementing
dedicated counters when reaching the executor path.
---
 src/include/nodes/execnodes.h                 |  1 +
 src/backend/executor/execUtils.c              |  1 +
 src/backend/executor/nodeGather.c             |  3 ++
 src/backend/executor/nodeGatherMerge.c        |  2 +
 doc/src/sgml/pgstatstatements.sgml            | 18 +++++++++
 .../expected/oldextversions.out               |  2 +
 .../pg_stat_statements/expected/parallel.out  | 10 +++--
 .../pg_stat_statements--1.11--1.12.sql        |  2 +
 .../pg_stat_statements/pg_stat_statements.c   | 38 +++++++++++++++----
 contrib/pg_stat_statements/sql/parallel.sql   |  4 +-
 10 files changed, 68 insertions(+), 13 deletions(-)

diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e4698a28c4..2a81fb1ead 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -707,6 +707,7 @@ typedef struct EState
 	struct EPQState *es_epq_active;
 
 	bool		es_use_parallel_mode;	/* can we use parallel workers? */
+	bool		es_used_parallel_mode;	/* was executed in parallel */
 
 	int			es_parallel_workers_to_launch;	/* number of workers to
 												 * launch. */
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 6712302ec8..f06d4eefd3 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -158,6 +158,7 @@ CreateExecutorState(void)
 	estate->es_sourceText = NULL;
 
 	estate->es_use_parallel_mode = false;
+	estate->es_used_parallel_mode = false;
 	estate->es_parallel_workers_to_launch = 0;
 	estate->es_parallel_workers_launched = 0;
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 7f7edc7f9f..f33c837304 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -182,6 +182,9 @@ ExecGather(PlanState *pstate)
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
 
+			if (pcxt->nworkers_launched > 0)
+				estate->es_used_parallel_mode = true;
+
 			/*
 			 * Count number of workers originally wanted and actually
 			 * launched.
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index bc99c0b448..671d6edb04 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -222,6 +222,8 @@ ExecGatherMerge(PlanState *pstate)
 			LaunchParallelWorkers(pcxt);
 			/* We save # workers launched for the benefit of EXPLAIN */
 			node->nworkers_launched = pcxt->nworkers_launched;
+			 if (pcxt->nworkers_launched > 0)
+				 estate->es_used_parallel_mode = true;
 
 			/*
 			 * Count number of workers originally wanted and actually
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 501b468e9a..4af09837cf 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -545,6 +545,24 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_queries_planned</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement was planned to use parallelism.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parallel_queries_launched</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times that the statement was executed using parallelism
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 0c60fc8127..84e3f04d5f 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -397,6 +397,8 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
  jit_deform_time            | double precision         |           |          | 
  parallel_workers_to_launch | bigint                   |           |          | 
  parallel_workers_launched  | bigint                   |           |          | 
+ parallel_queries_planned   | bigint                   |           |          | 
+ parallel_queries_launched  | bigint                   |           |          | 
  stats_since                | timestamp with time zone |           |          | 
  minmax_stats_since         | timestamp with time zone |           |          | 
 
diff --git a/contrib/pg_stat_statements/expected/parallel.out b/contrib/pg_stat_statements/expected/parallel.out
index 8af3bd2c91..c9b1a1e339 100644
--- a/contrib/pg_stat_statements/expected/parallel.out
+++ b/contrib/pg_stat_statements/expected/parallel.out
@@ -22,13 +22,15 @@ SELECT count(*) FROM pgss_parallel_tab;
 
 SELECT query,
   parallel_workers_to_launch > 0 AS has_workers_to_launch,
-  parallel_workers_launched > 0 AS has_workers_launched
+  parallel_workers_launched > 0 AS has_workers_launched,
+  parallel_queries_planned > 0 AS has_queries_planned,
+  parallel_queries_launched > 0 AS has_queries_launched
   FROM pg_stat_statements
   WHERE query ~ 'SELECT count'
   ORDER BY query COLLATE "C";
-                 query                  | has_workers_to_launch | has_workers_launched 
-----------------------------------------+-----------------------+----------------------
- SELECT count(*) FROM pgss_parallel_tab | t                     | t
+                 query                  | has_workers_to_launch | has_workers_launched | has_queries_planned | has_queries_launched 
+----------------------------------------+-----------------------+----------------------+---------------------+----------------------
+ SELECT count(*) FROM pgss_parallel_tab | t                     | t                    | t                   | t
 (1 row)
 
 DROP TABLE pgss_parallel_tab;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
index 80e6be2544..3a3964a998 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.11--1.12.sql
@@ -62,6 +62,8 @@ CREATE FUNCTION pg_stat_statements(IN showtext boolean,
     OUT jit_deform_time float8,
     OUT parallel_workers_to_launch int8,
     OUT parallel_workers_launched int8,
+    OUT parallel_queries_planned int8,
+    OUT parallel_queries_launched int8,
     OUT stats_since timestamp with time zone,
     OUT minmax_stats_since timestamp with time zone
 )
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 773b821911..072db1281e 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -209,6 +209,10 @@ typedef struct Counters
 											 * be launched */
 	int64       parallel_workers_launched;	/* # of parallel workers actually
 											 * launched */
+	int64       parallel_queries_planned;	/* # of times query was planned
+												 * to use parallelism */
+	int64       parallel_queries_launched;	/* # of times query was executed
+												 * using parallelism */
 } Counters;
 
 /*
@@ -355,7 +359,9 @@ static void pgss_store(const char *query, uint64 queryId,
 					   const struct JitInstrumentation *jitusage,
 					   JumbleState *jstate,
 					   int parallel_workers_to_launch,
-					   int parallel_workers_launched);
+					   int parallel_workers_launched,
+					   bool parallel_queries_planned,
+					   bool parallel_queries_launched);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 										pgssVersion api_version,
 										bool showtext);
@@ -877,7 +883,9 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
 				   NULL,
 				   jstate,
 				   0,
-				   0);
+				   0,
+				   false,
+				   false);
 }
 
 /*
@@ -957,7 +965,9 @@ pgss_planner(Query *parse,
 				   NULL,
 				   NULL,
 				   0,
-				   0);
+				   0,
+				   false,
+				   false);
 	}
 	else
 	{
@@ -1092,7 +1102,9 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 				   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
 				   NULL,
 				   queryDesc->estate->es_parallel_workers_to_launch,
-				   queryDesc->estate->es_parallel_workers_launched);
+				   queryDesc->estate->es_parallel_workers_launched,
+				   queryDesc->plannedstmt->parallelModeNeeded,
+				   queryDesc->estate->es_used_parallel_mode);
 	}
 
 	if (prev_ExecutorEnd)
@@ -1225,7 +1237,9 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				   NULL,
 				   NULL,
 				   0,
-				   0);
+				   0,
+				   false,
+				   false);
 	}
 	else
 	{
@@ -1288,7 +1302,9 @@ pgss_store(const char *query, uint64 queryId,
 		   const struct JitInstrumentation *jitusage,
 		   JumbleState *jstate,
 		   int parallel_workers_to_launch,
-		   int parallel_workers_launched)
+		   int parallel_workers_launched,
+		   bool parallel_queries_planned,
+		   bool parallel_queries_launched)
 {
 	pgssHashKey key;
 	pgssEntry  *entry;
@@ -1494,6 +1510,10 @@ pgss_store(const char *query, uint64 queryId,
 		/* parallel worker counters */
 		entry->counters.parallel_workers_to_launch += parallel_workers_to_launch;
 		entry->counters.parallel_workers_launched += parallel_workers_launched;
+		if (parallel_queries_planned)
+			entry->counters.parallel_queries_planned += 1;
+		if (parallel_queries_launched)
+			entry->counters.parallel_queries_launched += 1;
 
 		SpinLockRelease(&entry->mutex);
 	}
@@ -1561,8 +1581,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_9	33
 #define PG_STAT_STATEMENTS_COLS_V1_10	43
 #define PG_STAT_STATEMENTS_COLS_V1_11	49
-#define PG_STAT_STATEMENTS_COLS_V1_12	51
-#define PG_STAT_STATEMENTS_COLS			51	/* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_12	53
+#define PG_STAT_STATEMENTS_COLS			53	/* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1974,6 +1994,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 		{
 			values[i++] = Int64GetDatumFast(tmp.parallel_workers_to_launch);
 			values[i++] = Int64GetDatumFast(tmp.parallel_workers_launched);
+			values[i++] = Int64GetDatumFast(tmp.parallel_queries_planned);
+			values[i++] = Int64GetDatumFast(tmp.parallel_queries_launched);
 		}
 		if (api_version >= PGSS_V1_11)
 		{
diff --git a/contrib/pg_stat_statements/sql/parallel.sql b/contrib/pg_stat_statements/sql/parallel.sql
index 4ce1573d13..74a5bb5195 100644
--- a/contrib/pg_stat_statements/sql/parallel.sql
+++ b/contrib/pg_stat_statements/sql/parallel.sql
@@ -18,7 +18,9 @@ SELECT count(*) FROM pgss_parallel_tab;
 
 SELECT query,
   parallel_workers_to_launch > 0 AS has_workers_to_launch,
-  parallel_workers_launched > 0 AS has_workers_launched
+  parallel_workers_launched > 0 AS has_workers_launched,
+  parallel_queries_planned > 0 AS has_queries_planned,
+  parallel_queries_launched > 0 AS has_queries_launched
   FROM pg_stat_statements
   WHERE query ~ 'SELECT count'
   ORDER BY query COLLATE "C";
-- 
2.45.2

#7Guillaume Lelarge
guillaume@lelarge.info
In reply to: Michael Paquier (#6)
Re: Add parallel columns for pg_stat_statements

Hi,

Le mar. 8 oct. 2024 à 09:29, Michael Paquier <michael@paquier.xyz> a écrit :

On Mon, Oct 07, 2024 at 10:00:13AM +0200, Guillaume Lelarge wrote:

Le lun. 7 oct. 2024 à 02:18, Michael Paquier <michael@paquier.xyz> a

écrit :

I'd recommend to split that into more independent patches:
- Introduce the two counters in EState with the incrementations done
in nodeGatherMerge.c and nodeGather.c (mentioned that at [2], you may
want to coordinate with Benoit to avoid duplicating the work).
- Expand pg_stat_statements to use them for DMLs, SELECTs, well where
they matter.
- Look at expanding that for utilities that can do parallel jobs:
CREATE INDEX and VACUUM, but this has lower priority to me, and this
can reuse the same counters as the ones added by patch 2.

The first two are done. The last one is beyond my scope.

That's fair. I have put my hands on this patch set, finishing with
the attached.

A couple of notes:
- I've been struggling a bit on the "planned" vs "launched" terms used
in the names for the counters. It is inconsistent with the backend
state, where we talk about workers "to launch" and workers "launched".
"planned" does not really apply to utilities, as this may not be
planned per se.

You're right. Much better to keep them consistent.

- The test in parallel.sql can be cheaper, tweaking the right GUCs the
right way data in the table is not even required to spawn a set of
parallel workers.

Yeah, it could be cheaper. As it is already quick, I didn't do the extra
mile to make it cheaper.

- Meson was not updated for the new test and the files to install.

Oops, sorry. Didn't know that Meson had to be updated.

0001 and 0002 are the parts of the patch that I can see myself
applying; it is pretty cool to see pg_stat_statements complain that
the launched/to_launch ratio can get unbalanced really quickly when I
do something stupid. The CI is stable with these.

Great, thanks :)

0003 has the remaining bits with the 3rd and 4th counters, able to
apply on top of 0002.

Still think they are interesting but I understand your concerns.

I've done a bit of testing with the three patches, and didn't find any
issue with them.

--
Guillaume.

#8Michael Paquier
michael@paquier.xyz
In reply to: Guillaume Lelarge (#7)
Re: Add parallel columns for pg_stat_statements

On Tue, Oct 08, 2024 at 03:53:16PM +0200, Guillaume Lelarge wrote:

I've done a bit of testing with the three patches, and didn't find any
issue with them.

Okay, applied 0001 and 0002 then after a second lookup. I'll spend
some more time thinking about 0003 and the other threads.
--
Michael

#9Guillaume Lelarge
guillaume@lelarge.info
In reply to: Michael Paquier (#8)
Re: Add parallel columns for pg_stat_statements

Le mer. 9 oct. 2024 à 01:33, Michael Paquier <michael@paquier.xyz> a écrit :

On Tue, Oct 08, 2024 at 03:53:16PM +0200, Guillaume Lelarge wrote:

I've done a bit of testing with the three patches, and didn't find any
issue with them.

Okay, applied 0001 and 0002 then after a second lookup. I'll spend
some more time thinking about 0003 and the other threads.

Thanks a lot.

--
Guillaume.

#10Michael Paquier
michael@paquier.xyz
In reply to: Michael Paquier (#8)
Re: Add parallel columns for pg_stat_statements

On Wed, Oct 09, 2024 at 08:32:52AM +0900, Michael Paquier wrote:

Okay, applied 0001 and 0002 then after a second lookup. I'll spend
some more time thinking about 0003 and the other threads.

Considered 0003, and I'm still not sure that this is something that
is really required based on the correlation that are now possible with
the number of times a query has been called and the number of
planned/launched workers.

So I am marking the entry as committed. Let's see about the threads
for the addition of this data at table-level and at the
database-level.
--
Michael

#11Guillaume Lelarge
guillaume@lelarge.info
In reply to: Michael Paquier (#10)
Re: Add parallel columns for pg_stat_statements

Le jeu. 7 nov. 2024 à 04:19, Michael Paquier <michael@paquier.xyz> a écrit :

On Wed, Oct 09, 2024 at 08:32:52AM +0900, Michael Paquier wrote:

Okay, applied 0001 and 0002 then after a second lookup. I'll spend
some more time thinking about 0003 and the other threads.

Considered 0003, and I'm still not sure that this is something that
is really required based on the correlation that are now possible with
the number of times a query has been called and the number of
planned/launched workers.

I'm fine with your decision. After using the new metrics, we'll probably
see more clearly if that's enough.

So I am marking the entry as committed. Let's see about the threads
for the addition of this data at table-level and at the
database-level.

Sounds good!

Table level is probably not the most important in my view. Database-level
and logging are what really matters to me.

Thanks.

--
Guillaume.