From: Drouvot, Bertrand <bdrouvot@amazon.com>
To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Cc: Schneider (AWS), Jeremy <schnjere@amazon.com>
Subject: [PATCH] Query Jumbling for CALL and SET utility statements
Date: Wed, 31 Aug 2022 17:33:44 +0200
Message-ID: <36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com> (raw)
Hi hackers,
While query jumbling is provided for function calls that’s currently not
the case for procedures calls.
The reason behind this is that all utility statements are currently
discarded for jumbling.
We’ve recently seen performance impacts (LWLock contention) due to the
lack of jumbling on procedure calls with pg_stat_statements and
pg_stat_statements.track_utility enabled (think an application with a
high rate of procedure calls with unique parameters for each call).
Jeremy has had this conversation on twitter (see
https://twitter.com/jer_s/status/1560003560116342785) and Nikolay
reported that he also had to work on a similar performance issue with
SET being used.
That’s why we think it would make sense to allow jumbling for those 2
utility statements: CALL and SET.
Please find attached a patch proposal for doing so.
With the attached patch we would get things like:
CALL MINUS_TWO(3);
CALL MINUS_TWO(7);
CALL SUM_TWO(3, 8);
CALL SUM_TWO(7, 5);
set enable_seqscan=false;
set enable_seqscan=true;
set seq_page_cost=2.0;
set seq_page_cost=1.0;
postgres=# SELECT query, calls, rows FROM pg_stat_statements;
query | calls | rows
-----------------------------------+-------+------
set seq_page_cost=$1 | 2 | 0
CALL MINUS_TWO($1) | 2 | 0
set enable_seqscan=$1 | 2 | 0
CALL SUM_TWO($1, $2) | 2 | 0
Looking forward to your feedback,
Thanks,
Jeremy & Bertrand
--
Bertrand Drouvot
Amazon Web Services:https://aws.amazon.comAttachments:v1-0001-JumbleQuery-on-Call-and-Set.patch (text/plain, 10.8K) downloaddiff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.outindex ff0166fb9d..bdb83f5f72 100644--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out@@ -272,7 +272,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
wal_records > $2 as wal_records_generated, +| | | | |
wal_records >= rows as wal_records_ge_rows +| | | | |
FROM pg_stat_statements ORDER BY query COLLATE "C" | | | | |
- SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t+ SET pg_stat_statements.track_utility = $1 | 1 | 0 | f | f | t
UPDATE pgss_test SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(7 rows)
@@ -423,6 +423,101 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
+-- PL/pgSQL procedure and pg_stat_statements.track = top+CREATE PROCEDURE MINUS_TWO(i INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (i - 1 - 1.0)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;+CREATE PROCEDURE SUM_TWO(i INTEGER, j INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (j + j)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;+SET pg_stat_statements.track = 'top';+SET pg_stat_statements.track_utility = TRUE;+SELECT pg_stat_statements_reset();+ pg_stat_statements_reset +--------------------------+ +(1 row)++CALL MINUS_TWO(3);+CALL MINUS_TWO(7);+CALL SUM_TWO(3, 8);+CALL SUM_TWO(7, 5);+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";+ query | calls | rows +------------------------------------------------------------------------------+-------+------+ CALL MINUS_TWO($1) | 2 | 0+ CALL SUM_TWO($1, $2) | 2 | 0+ SELECT pg_stat_statements_reset() | 1 | 1+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0+(4 rows)++-- PL/pgSQL procedure and pg_stat_statements.track = all+-- we drop and recreate the procedures to avoid any caching funnies+SET pg_stat_statements.track_utility = FALSE;+DROP PROCEDURE MINUS_TWO(INTEGER);+DROP PROCEDURE SUM_TWO(INTEGER, INTEGER);+CREATE PROCEDURE MINUS_TWO(i INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (i - 1 - 1.0)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;+CREATE PROCEDURE SUM_TWO(i INTEGER, j INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (j + j)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;+SET pg_stat_statements.track = 'all';+SET pg_stat_statements.track_utility = TRUE;+SELECT pg_stat_statements_reset();+ pg_stat_statements_reset +--------------------------+ +(1 row)++CALL MINUS_TWO(3);+CALL MINUS_TWO(7);+CALL SUM_TWO(3, 8);+CALL SUM_TWO(7, 5);+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";+ query | calls | rows +------------------------------------------------------------------------------+-------+------+ CALL MINUS_TWO($1) | 2 | 0+ CALL SUM_TWO($1, $2) | 2 | 0+ SELECT (i - $2 - $3)::INTEGER | 2 | 2+ SELECT (j + j)::INTEGER | 2 | 2+ SELECT pg_stat_statements_reset() | 1 | 1+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0+(6 rows)++-- SET+SELECT pg_stat_statements_reset();+ pg_stat_statements_reset +--------------------------+ +(1 row)++set enable_seqscan=false;+set enable_seqscan=true;+set seq_page_cost=2.0;+set seq_page_cost=1.0;+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";+ query | calls | rows +------------------------------------------------------------------------------+-------+------+ SELECT pg_stat_statements_reset() | 1 | 1+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0+ set enable_seqscan=$1 | 2 | 0+ set seq_page_cost=$1 | 2 | 0+(4 rows)++SET pg_stat_statements.track_utility = FALSE;
--
-- queries with locking clauses
--
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.cindex ba868f0de9..b98d9b60df 100644--- a/contrib/pg_stat_statements/pg_stat_statements.c+++ b/contrib/pg_stat_statements/pg_stat_statements.c@@ -832,7 +832,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
* inherit from the underlying statement's one (except DEALLOCATE which is
* entirely untracked).
*/
- if (query->utilityStmt)+ if (query->utilityStmt && !JUMBLE_UTILITY(query->utilityStmt))
{
if (pgss_track_utility && !PGSS_HANDLED_UTILITY(query->utilityStmt))
query->queryId = UINT64CONST(0);
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sqlindex a01f183727..88d54fea11 100644--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql@@ -201,6 +201,71 @@ SELECT PLUS_ONE(1);
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+-- PL/pgSQL procedure and pg_stat_statements.track = top+CREATE PROCEDURE MINUS_TWO(i INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (i - 1 - 1.0)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;++CREATE PROCEDURE SUM_TWO(i INTEGER, j INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (j + j)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;++SET pg_stat_statements.track = 'top';+SET pg_stat_statements.track_utility = TRUE;+SELECT pg_stat_statements_reset();+CALL MINUS_TWO(3);+CALL MINUS_TWO(7);+CALL SUM_TWO(3, 8);+CALL SUM_TWO(7, 5);++SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";++-- PL/pgSQL procedure and pg_stat_statements.track = all+-- we drop and recreate the procedures to avoid any caching funnies+SET pg_stat_statements.track_utility = FALSE;+DROP PROCEDURE MINUS_TWO(INTEGER);+DROP PROCEDURE SUM_TWO(INTEGER, INTEGER);++CREATE PROCEDURE MINUS_TWO(i INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (i - 1 - 1.0)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;++CREATE PROCEDURE SUM_TWO(i INTEGER, j INTEGER) AS $$+DECLARE+ r INTEGER;+BEGIN+ SELECT (j + j)::INTEGER INTO r;+END; $$ LANGUAGE plpgsql;++SET pg_stat_statements.track = 'all';+SET pg_stat_statements.track_utility = TRUE;+SELECT pg_stat_statements_reset();+CALL MINUS_TWO(3);+CALL MINUS_TWO(7);+CALL SUM_TWO(3, 8);+CALL SUM_TWO(7, 5);++SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";+-- SET+SELECT pg_stat_statements_reset();+set enable_seqscan=false;+set enable_seqscan=true;+set seq_page_cost=2.0;+set seq_page_cost=1.0;++SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";++SET pg_stat_statements.track_utility = FALSE;+
--
-- queries with locking clauses
--
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgmlindex ecf6cd6bf3..60dd153a47 100644--- a/doc/src/sgml/pgstatstatements.sgml+++ b/doc/src/sgml/pgstatstatements.sgml@@ -492,7 +492,7 @@
structures according to an internal hash calculation. Typically, two
queries will be considered the same for this purpose if they are
semantically equivalent except for the values of literal constants
- appearing in the query. Utility commands (that is, all other commands)+ appearing in the query. Utility commands (that is, all other commands) except CALL and SET
are compared strictly on the basis of their textual query strings, however.
</para>
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/utils/misc/queryjumble.cindex eeaa0b31fe..8d526c9e9a 100644--- a/src/backend/utils/misc/queryjumble.c+++ b/src/backend/utils/misc/queryjumble.c@@ -104,7 +104,7 @@ JumbleQuery(Query *query, const char *querytext)
Assert(IsQueryIdEnabled());
- if (query->utilityStmt)+ if (query->utilityStmt && !JUMBLE_UTILITY(query->utilityStmt))
{
query->queryId = compute_utility_query_id(querytext,
query->stmt_location,
@@ -240,10 +240,11 @@ static void
JumbleQueryInternal(JumbleState *jstate, Query *query)
{
Assert(IsA(query, Query));
- Assert(query->utilityStmt == NULL);+ Assert(query->utilityStmt == NULL || JUMBLE_UTILITY(query->utilityStmt));
APP_JUMB(query->commandType);
/* resultRelation is usually predictable from commandType */
+ JumbleExpr(jstate, (Node *) query->utilityStmt);
JumbleExpr(jstate, (Node *) query->cteList);
JumbleRangeTable(jstate, query->rtable);
JumbleExpr(jstate, (Node *) query->jointree);
@@ -383,6 +384,30 @@ JumbleExpr(JumbleState *jstate, Node *node)
APP_JUMB(var->varlevelsup);
}
break;
+ case T_CallStmt:+ {+ CallStmt *stmt = (CallStmt *) node;+ FuncExpr *expr = stmt->funcexpr;++ APP_JUMB(expr->funcid);+ JumbleExpr(jstate, (Node *) expr->args);+ }+ break;+ case T_VariableSetStmt:+ {+ VariableSetStmt *stmt = (VariableSetStmt *) node;++ APP_JUMB_STRING(stmt->name);+ JumbleExpr(jstate, (Node *) stmt->args);+ }+ break;+ case T_A_Const:+ {+ int loc = ((const A_Const *) node)->location;++ RecordConstLocation(jstate, loc);+ }+ break;
case T_Const:
{
Const *c = (Const *) node;
diff --git a/src/include/utils/queryjumble.h b/src/include/utils/queryjumble.hindex 3c2d9beab2..cada9a1ed3 100644--- a/src/include/utils/queryjumble.h+++ b/src/include/utils/queryjumble.h@@ -18,6 +18,12 @@
#define JUMBLE_SIZE 1024 /* query serialization buffer size */
+/*+ * Jumble those utility statements+ */+#define JUMBLE_UTILITY(n) (IsA(n, CallStmt) || \+ IsA(n, VariableSetStmt))+
/*
* Struct for tracking locations/lengths of constants during normalization
*/
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: bdrouvot@amazon.com, pgsql-hackers@lists.postgresql.org, schnjere@amazon.com
Subject: Re: [PATCH] Query Jumbling for CALL and SET utility statements
In-Reply-To: <36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox