pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
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.com

Attachments:

v1-0001-JumbleQuery-on-Call-and-Set.patch (text/plain, 10.8K) download diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out index 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.c index 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.sql index 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.sgml index 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.c index 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.h index 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 */

view thread (45+ messages)  latest in thread

Message-ID: <36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com>
Permalink:  ../36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com/
Also on:    postgresql.org/message-id/36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com

 ·  · 

reply

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