agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v11 1/7] Row pattern recognition patch for raw parser.
12+ messages / 2 participants
[nested] [flat]

* [PATCH v11 1/7] Row pattern recognition patch for raw parser.
@ 2023-11-08 06:57 Tatsuo Ishii <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Tatsuo Ishii @ 2023-11-08 06:57 UTC (permalink / raw)

---
 src/backend/parser/gram.y       | 222 ++++++++++++++++++++++++++++++--
 src/include/nodes/parsenodes.h  |  56 ++++++++
 src/include/parser/kwlist.h     |   8 ++
 src/include/parser/parse_node.h |   1 +
 4 files changed, 273 insertions(+), 14 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c224df4ecc..e09eb061f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -251,6 +251,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	DefElem	   *defelt;
 	SortBy	   *sortby;
 	WindowDef  *windef;
+	RPCommonSyntax	*rpcom;
+	RPSubsetItem	*rpsubset;
 	JoinExpr   *jexpr;
 	IndexElem  *ielem;
 	StatsElem  *selem;
@@ -278,6 +280,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	MergeWhenClause *mergewhen;
 	struct KeyActions *keyactions;
 	struct KeyAction *keyaction;
+	RPSkipTo	skipto;
 }
 
 %type <node>	stmt toplevel_stmt schema_stmt routine_body_stmt
@@ -453,8 +456,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				TriggerTransitions TriggerReferencing
 				vacuum_relation_list opt_vacuum_relation_list
 				drop_option_list pub_obj_list
-
-%type <node>	opt_routine_body
+				row_pattern_measure_list row_pattern_definition_list
+				opt_row_pattern_subset_clause
+				row_pattern_subset_list row_pattern_subset_rhs
+				row_pattern
+%type <rpsubset>	 row_pattern_subset_item
+%type <node>	opt_routine_body row_pattern_term
 %type <groupclause> group_clause
 %type <list>	group_by_list
 %type <node>	group_by_item empty_grouping_set rollup_clause cube_clause
@@ -551,6 +558,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <range>	relation_expr_opt_alias
 %type <node>	tablesample_clause opt_repeatable_clause
 %type <target>	target_el set_target insert_column_item
+				row_pattern_measure_item row_pattern_definition
+%type <skipto>	first_or_last
 
 %type <str>		generic_option_name
 %type <node>	generic_option_arg
@@ -633,6 +642,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>	window_clause window_definition_list opt_partition_clause
 %type <windef>	window_definition over_clause window_specification
 				opt_frame_clause frame_extent frame_bound
+%type <rpcom>	opt_row_pattern_common_syntax opt_row_pattern_skip_to
+%type <boolean>	opt_row_pattern_initial_or_seek
+%type <list>	opt_row_pattern_measures
 %type <ival>	opt_window_exclusion_clause
 %type <str>		opt_existing_window_name
 %type <boolean> opt_if_not_exists
@@ -659,7 +671,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				json_object_constructor_null_clause_opt
 				json_array_constructor_null_clause_opt
 
-
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
  * They must be listed first so that their numeric codes do not depend on
@@ -702,7 +713,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
 
 	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
-	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC
+	DEFERRABLE DEFERRED DEFINE DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC
 	DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
 	DOUBLE_P DROP
 
@@ -718,7 +729,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
 	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE
-	INCLUDING INCREMENT INDENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
+	INCLUDING INCREMENT INDENT INDEX INDEXES INHERIT INHERITS INITIAL INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
@@ -731,7 +742,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
-	MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD
+	MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MEASURES MERGE METHOD
 	MINUTE_P MINVALUE MODE MONTH_P MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE
@@ -743,8 +754,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	ORDER ORDINALITY OTHERS OUT_P OUTER_P
 	OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
 
-	PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD
-	PLACING PLANS POLICY
+	PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PAST
+	PATTERN_P PERMUTE PLACING PLANS POLICY
 	POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
 	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
 
@@ -755,12 +766,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
 	ROUTINE ROUTINES ROW ROWS RULE
 
-	SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
+	SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SEEK SELECT
 	SEQUENCE SEQUENCES
+
 	SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
 	SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
 	START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P
-	SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER
+	SUBSCRIPTION SUBSET SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER
 
 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
 	TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM
@@ -853,6 +865,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 %nonassoc	UNBOUNDED		/* ideally would have same precedence as IDENT */
 %nonassoc	IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
+%nonassoc	MEASURES AFTER INITIAL SEEK PATTERN_P
 %left		Op OPERATOR		/* multi-character ops and user-defined operators */
 %left		'+' '-'
 %left		'*' '/' '%'
@@ -15901,7 +15914,8 @@ over_clause: OVER window_specification
 		;
 
 window_specification: '(' opt_existing_window_name opt_partition_clause
-						opt_sort_clause opt_frame_clause ')'
+						opt_sort_clause opt_row_pattern_measures opt_frame_clause
+						opt_row_pattern_common_syntax ')'
 				{
 					WindowDef  *n = makeNode(WindowDef);
 
@@ -15909,10 +15923,12 @@ window_specification: '(' opt_existing_window_name opt_partition_clause
 					n->refname = $2;
 					n->partitionClause = $3;
 					n->orderClause = $4;
+					n->rowPatternMeasures = $5;
 					/* copy relevant fields of opt_frame_clause */
-					n->frameOptions = $5->frameOptions;
-					n->startOffset = $5->startOffset;
-					n->endOffset = $5->endOffset;
+					n->frameOptions = $6->frameOptions;
+					n->startOffset = $6->startOffset;
+					n->endOffset = $6->endOffset;
+					n->rpCommonSyntax = $7;
 					n->location = @1;
 					$$ = n;
 				}
@@ -15936,6 +15952,31 @@ opt_partition_clause: PARTITION BY expr_list		{ $$ = $3; }
 			| /*EMPTY*/								{ $$ = NIL; }
 		;
 
+/*
+ * ROW PATTERN_P MEASURES
+ */
+opt_row_pattern_measures: MEASURES row_pattern_measure_list	{ $$ = $2; }
+			| /*EMPTY*/								{ $$ = NIL; }
+		;
+
+row_pattern_measure_list:
+			row_pattern_measure_item
+					{ $$ = list_make1($1); }
+			| row_pattern_measure_list ',' row_pattern_measure_item
+					{ $$ = lappend($1, $3); }
+		;
+
+row_pattern_measure_item:
+			a_expr AS ColLabel
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = $3;
+					$$->indirection = NIL;
+					$$->val = (Node *) $1;
+					$$->location = @1;
+				}
+		;
+
 /*
  * For frame clauses, we return a WindowDef, but only some fields are used:
  * frameOptions, startOffset, and endOffset.
@@ -16095,6 +16136,143 @@ opt_window_exclusion_clause:
 			| /*EMPTY*/				{ $$ = 0; }
 		;
 
+opt_row_pattern_common_syntax:
+opt_row_pattern_skip_to opt_row_pattern_initial_or_seek
+				PATTERN_P '(' row_pattern ')'
+				opt_row_pattern_subset_clause
+				DEFINE row_pattern_definition_list
+			{
+				RPCommonSyntax *n = makeNode(RPCommonSyntax);
+				n->rpSkipTo = $1->rpSkipTo;
+				n->rpSkipVariable = $1->rpSkipVariable;
+				n->initial = $2;
+				n->rpPatterns = $5;
+				n->rpSubsetClause = $7;
+				n->rpDefs = $9;
+				$$ = n;
+			}
+			| /*EMPTY*/		{ $$ = NULL; }
+	;
+
+opt_row_pattern_skip_to:
+			AFTER MATCH SKIP TO NEXT ROW
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					n->rpSkipTo = ST_NEXT_ROW;
+					n->rpSkipVariable = NULL;
+					$$ = n;
+			}
+			| AFTER MATCH SKIP PAST LAST_P ROW
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					n->rpSkipTo = ST_PAST_LAST_ROW;
+					n->rpSkipVariable = NULL;
+					$$ = n;
+				}
+			| AFTER MATCH SKIP TO first_or_last ColId
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					n->rpSkipTo = $5;
+					n->rpSkipVariable = $6;
+					$$ = n;
+				}
+/*
+			| AFTER MATCH SKIP TO LAST_P ColId		%prec LAST_P
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					n->rpSkipTo = ST_LAST_VARIABLE;
+					n->rpSkipVariable = $6;
+					$$ = n;
+				}
+			| AFTER MATCH SKIP TO ColId
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					n->rpSkipTo = ST_VARIABLE;
+					n->rpSkipVariable = $5;
+					$$ = n;
+				}
+*/
+			| /*EMPTY*/
+				{
+					RPCommonSyntax *n = makeNode(RPCommonSyntax);
+					/* temporary set default to ST_NEXT_ROW */
+					n->rpSkipTo = ST_PAST_LAST_ROW;
+					n->rpSkipVariable = NULL;
+					$$ = n;
+				}
+	;
+
+first_or_last:
+			FIRST_P		{ $$ = ST_FIRST_VARIABLE; }
+			| LAST_P	{ $$ = ST_LAST_VARIABLE; }
+	;
+
+opt_row_pattern_initial_or_seek:
+			INITIAL			{ $$ = true; }
+			| SEEK
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("SEEK is not supported"),
+							 errhint("Use INITIAL."),
+							 parser_errposition(@1)));
+				}
+			| /*EMPTY*/		{ $$ = true; }
+		;
+
+row_pattern:
+			row_pattern_term							{ $$ = list_make1($1); }
+			| row_pattern row_pattern_term				{ $$ = lappend($1, $2); }
+		;
+
+row_pattern_term:
+			ColId	{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "", (Node *)makeString($1), NULL, @1); }
+			| ColId '*'	{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", (Node *)makeString($1), NULL, @1); }
+			| ColId '+'	{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", (Node *)makeString($1), NULL, @1); }
+			| ColId '?'	{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "?", (Node *)makeString($1), NULL, @1); }
+		;
+
+opt_row_pattern_subset_clause:
+			SUBSET row_pattern_subset_list	{ $$ = $2; }
+			| /*EMPTY*/												{ $$ = NIL; }
+		;
+
+row_pattern_subset_list:
+			row_pattern_subset_item									{ $$ = list_make1($1); }
+			| row_pattern_subset_list ',' row_pattern_subset_item	{ $$ = lappend($1, $3); }
+			| /*EMPTY*/												{ $$ = NIL; }
+		;
+
+row_pattern_subset_item: ColId '=' '(' row_pattern_subset_rhs ')'
+			{
+				RPSubsetItem *n = makeNode(RPSubsetItem);
+				n->name = $1;
+				n->rhsVariable = $4;
+				$$ = n;
+			}
+		;
+
+row_pattern_subset_rhs:
+			ColId								{ $$ = list_make1(makeStringConst($1, @1)); }
+			| row_pattern_subset_rhs ',' ColId	{ $$ = lappend($1, makeStringConst($3, @1)); }
+			| /*EMPTY*/							{ $$ = NIL; }
+		;
+
+row_pattern_definition_list:
+			row_pattern_definition										{ $$ = list_make1($1); }
+			| row_pattern_definition_list ',' row_pattern_definition	{ $$ = lappend($1, $3); }
+		;
+
+row_pattern_definition:
+			ColId AS a_expr
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = $1;
+					$$->indirection = NIL;
+					$$->val = (Node *) $3;
+					$$->location = @1;
+				}
+		;
 
 /*
  * Supporting nonterminals for expressions.
@@ -17190,6 +17368,7 @@ unreserved_keyword:
 			| INDEXES
 			| INHERIT
 			| INHERITS
+			| INITIAL
 			| INLINE_P
 			| INPUT_P
 			| INSENSITIVE
@@ -17217,6 +17396,7 @@ unreserved_keyword:
 			| MATCHED
 			| MATERIALIZED
 			| MAXVALUE
+			| MEASURES
 			| MERGE
 			| METHOD
 			| MINUTE_P
@@ -17259,6 +17439,9 @@ unreserved_keyword:
 			| PARTITION
 			| PASSING
 			| PASSWORD
+			| PAST
+			| PATTERN_P
+			| PERMUTE
 			| PLANS
 			| POLICY
 			| PRECEDING
@@ -17309,6 +17492,7 @@ unreserved_keyword:
 			| SEARCH
 			| SECOND_P
 			| SECURITY
+			| SEEK
 			| SEQUENCE
 			| SEQUENCES
 			| SERIALIZABLE
@@ -17334,6 +17518,7 @@ unreserved_keyword:
 			| STRICT_P
 			| STRIP_P
 			| SUBSCRIPTION
+			| SUBSET
 			| SUPPORT
 			| SYSID
 			| SYSTEM_P
@@ -17521,6 +17706,7 @@ reserved_keyword:
 			| CURRENT_USER
 			| DEFAULT
 			| DEFERRABLE
+			| DEFINE
 			| DESC
 			| DISTINCT
 			| DO
@@ -17683,6 +17869,7 @@ bare_label_keyword:
 			| DEFAULTS
 			| DEFERRABLE
 			| DEFERRED
+			| DEFINE
 			| DEFINER
 			| DELETE_P
 			| DELIMITER
@@ -17758,6 +17945,7 @@ bare_label_keyword:
 			| INDEXES
 			| INHERIT
 			| INHERITS
+			| INITIAL
 			| INITIALLY
 			| INLINE_P
 			| INNER_P
@@ -17807,6 +17995,7 @@ bare_label_keyword:
 			| MATCHED
 			| MATERIALIZED
 			| MAXVALUE
+			| MEASURES
 			| MERGE
 			| METHOD
 			| MINVALUE
@@ -17860,6 +18049,9 @@ bare_label_keyword:
 			| PARTITION
 			| PASSING
 			| PASSWORD
+			| PAST
+			| PATTERN_P
+			| PERMUTE
 			| PLACING
 			| PLANS
 			| POLICY
@@ -17916,6 +18108,7 @@ bare_label_keyword:
 			| SCROLL
 			| SEARCH
 			| SECURITY
+			| SEEK
 			| SELECT
 			| SEQUENCE
 			| SEQUENCES
@@ -17947,6 +18140,7 @@ bare_label_keyword:
 			| STRICT_P
 			| STRIP_P
 			| SUBSCRIPTION
+			| SUBSET
 			| SUBSTRING
 			| SUPPORT
 			| SYMMETRIC
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8..094c603887 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -540,6 +540,44 @@ typedef struct SortBy
 	int			location;		/* operator location, or -1 if none/unknown */
 } SortBy;
 
+/*
+ * AFTER MATCH row pattern skip to types in row pattern common syntax
+ */
+typedef enum RPSkipTo
+{
+	ST_NONE,					/* AFTER MATCH omitted */
+	ST_NEXT_ROW,				/* SKIP TO NEXT ROW */
+	ST_PAST_LAST_ROW,			/* SKIP TO PAST LAST ROW */
+	ST_FIRST_VARIABLE,			/* SKIP TO FIRST variable name */
+	ST_LAST_VARIABLE,			/* SKIP TO LAST variable name */
+	ST_VARIABLE					/* SKIP TO variable name */
+} RPSkipTo;
+
+/*
+ * Row Pattern SUBSET clause item
+ */
+typedef struct RPSubsetItem
+{
+	NodeTag		type;
+	char	   *name;			/* Row Pattern SUBSET clause variable name */
+	List	   *rhsVariable;	/* Row Pattern SUBSET rhs variables (list of char *string) */
+} RPSubsetItem;
+
+/*
+ * RowPatternCommonSyntax - raw representation of row pattern common syntax
+ *
+ */
+typedef struct RPCommonSyntax
+{
+	NodeTag		type;
+	RPSkipTo	rpSkipTo;		/* Row Pattern AFTER MATCH SKIP type */
+	char	   *rpSkipVariable;	/* Row Pattern Skip To variable name, if any */
+	bool		initial;		/* true if <row pattern initial or seek> is initial */
+	List	   *rpPatterns;		/* PATTERN variables (list of A_Expr) */
+	List	   *rpSubsetClause;	/* row pattern subset clause (list of RPSubsetItem), if any */
+	List	   *rpDefs;			/* row pattern definitions clause (list of ResTarget) */
+} RPCommonSyntax;
+
 /*
  * WindowDef - raw representation of WINDOW and OVER clauses
  *
@@ -555,6 +593,8 @@ typedef struct WindowDef
 	char	   *refname;		/* referenced window name, if any */
 	List	   *partitionClause;	/* PARTITION BY expression list */
 	List	   *orderClause;	/* ORDER BY (list of SortBy) */
+	List	   *rowPatternMeasures;	/* row pattern measures (list of ResTarget) */
+	RPCommonSyntax *rpCommonSyntax;	/* row pattern common syntax */
 	int			frameOptions;	/* frame_clause options, see below */
 	Node	   *startOffset;	/* expression for starting bound, if any */
 	Node	   *endOffset;		/* expression for ending bound, if any */
@@ -1476,6 +1516,11 @@ typedef struct GroupingSet
  * the orderClause might or might not be copied (see copiedOrder); the framing
  * options are never copied, per spec.
  *
+ * "defineClause" is Row Pattern Recognition DEFINE clause (list of
+ * TargetEntry). TargetEntry.resname represents row pattern definition
+ * variable name. "patternVariable" and "patternRegexp" represents PATTERN
+ * clause.
+ *
  * The information relevant for the query jumbling is the partition clause
  * type and its bounds.
  */
@@ -1507,6 +1552,17 @@ typedef struct WindowClause
 	Index		winref;			/* ID referenced by window functions */
 	/* did we copy orderClause from refname? */
 	bool		copiedOrder pg_node_attr(query_jumble_ignore);
+	/* Row Pattern AFTER MACH SKIP clause */
+	RPSkipTo	rpSkipTo;		/* Row Pattern Skip To type */
+	bool		initial;		/* true if <row pattern initial or seek> is initial */
+	/* Row Pattern DEFINE clause (list of TargetEntry) */
+	List		*defineClause;
+	/* Row Pattern DEFINE variable initial names (list of String) */
+	List		*defineInitial;
+	/* Row Pattern PATTERN variable name (list of String) */
+	List		*patternVariable;
+	/* Row Pattern PATTERN regular expression quantifier ('+' or ''. list of String) */
+	List		*patternRegexp;
 } WindowClause;
 
 /*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 5984dcfa4b..2804333b53 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -128,6 +128,7 @@ PG_KEYWORD("default", DEFAULT, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("defaults", DEFAULTS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("deferrable", DEFERRABLE, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("deferred", DEFERRED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("define", DEFINE, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("definer", DEFINER, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("delete", DELETE_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -212,6 +213,7 @@ PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("initial", INITIAL, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
@@ -265,6 +267,7 @@ PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("matched", MATCHED, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("measures", MEASURES, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("merge", MERGE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD, AS_LABEL)
@@ -326,6 +329,9 @@ PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("past", PAST, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("pattern", PATTERN_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("permute", PERMUTE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -385,6 +391,7 @@ PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("security", SECURITY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("seek", SEEK, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("select", SELECT, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("sequence", SEQUENCE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("sequences", SEQUENCES, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -416,6 +423,7 @@ PG_KEYWORD("stored", STORED, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("subset", SUBSET, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index f589112d5e..6640090910 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -51,6 +51,7 @@ typedef enum ParseExprKind
 	EXPR_KIND_WINDOW_FRAME_RANGE,	/* window frame clause with RANGE */
 	EXPR_KIND_WINDOW_FRAME_ROWS,	/* window frame clause with ROWS */
 	EXPR_KIND_WINDOW_FRAME_GROUPS,	/* window frame clause with GROUPS */
+	EXPR_KIND_RPR_DEFINE,		/* DEFINE */
 	EXPR_KIND_SELECT_TARGET,	/* SELECT target list item */
 	EXPR_KIND_INSERT_TARGET,	/* INSERT target list item */
 	EXPR_KIND_UPDATE_SOURCE,	/* UPDATE assignment source item */
-- 
2.25.1


----Next_Part(Wed_Nov__8_16_37_05_2023_872)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="v11-0002-Row-pattern-recognition-patch-parse-analysis.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v5 3/4] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.9% src/backend/replication/
  22.8% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 24d7ef795cb..1c5ffcab3e0 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -572,16 +572,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0e6a3d200c..74102def9c7 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1825,7 +1821,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1846,7 +1841,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1957,8 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1967,15 +1960,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2878,8 +2862,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2974,9 +2956,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2984,7 +2963,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -2995,21 +2973,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..feae2ae5f44 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..c058a5f9b1f 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index d7e62e4d488..dda872f7074 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--WLtTrIUEfQDmERxy
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0004-Add-FLUSH_MIXED-support-and-implement-it-for-RELA.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v6 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.4% src/test/recovery/t/
   3.5% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 11b7c114d3b..953ba97ed00 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..c058a5f9b1f 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..bceec2adede 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--DxqFthphbM+ha9Dy
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v6-0005-Change-RELATION-and-DATABASE-stats-to-anytime-flu.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v7 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 11b7c114d3b..953ba97ed00 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..cfa095ff0a8 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..29bae5e1121 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--C2xzVbxFmVrP7k6O
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v7-0005-Change-RELATION-and-DATABASE-stats-to-anytime-flu.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v9 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 11b7c114d3b..953ba97ed00 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..cfa095ff0a8 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..29bae5e1121 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--aq/6bi8L6WORnI+9
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v9-0005-Change-RELATION-and-DATABASE-stats-to-anytime-flu.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v10 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index aecc7a127e6..edf5ac65660 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..cfa095ff0a8 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..29bae5e1121 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--NVvBxFuyV/R+1/8/
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v10-0005-Change-RELATION-and-DATABASE-stats-to-anytime-fl.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v11 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index aecc7a127e6..edf5ac65660 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..cfa095ff0a8 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..29bae5e1121 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--2MEBAGW8+kohXisi
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v11-0005-Change-RELATION-and-DATABASE-stats-to-anytime-fl.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v1 2/3] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 3 files changed, 2 insertions(+), 57 deletions(-)
  75.3% src/backend/replication/
  24.6% src/backend/utils/activity/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a41453530a1..266379c780a 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -553,16 +553,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 1ab09655a70..c33185bd337 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1826,7 +1822,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1847,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1958,8 +1952,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1968,15 +1961,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2879,8 +2863,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2975,9 +2957,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2985,7 +2964,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -2996,21 +2974,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..feae2ae5f44 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
-- 
2.34.1


--tjEWjIIwfNIHQLgt
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0003-Add-FLUSH_MIXED-support-and-implement-it-for-RELA.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v8 4/5] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.4% src/backend/replication/
  23.4% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 11b7c114d3b..953ba97ed00 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -571,16 +571,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a7214d0dc6f..9a136e35b48 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1846,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1867,7 +1862,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1973,8 +1967,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1983,15 +1976,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false, true);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2894,8 +2878,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2985,9 +2967,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2995,7 +2974,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -3006,21 +2984,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false, true);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04d21483d93..ae2952cae89 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false, true);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO, true);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..cfa095ff0a8 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index 7d41715ed81..29bae5e1121 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval = '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--OI5irqZsxWxBW9nT
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0005-Change-RELATION-and-DATABASE-stats-to-anytime-flu.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v2 2/3] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 3 files changed, 2 insertions(+), 57 deletions(-)
  75.3% src/backend/replication/
  24.6% src/backend/utils/activity/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a41453530a1..266379c780a 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -553,16 +553,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 1ab09655a70..c33185bd337 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1826,7 +1822,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1847,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1958,8 +1952,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1968,15 +1961,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2879,8 +2863,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2975,9 +2957,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2985,7 +2964,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -2996,21 +2974,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..feae2ae5f44 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
-- 
2.34.1


--OFsrIl+bjhifp5hk
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0003-Add-FLUSH_MIXED-support-and-implement-it-for-RELA.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v3 2/3] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 3 files changed, 2 insertions(+), 57 deletions(-)
  75.3% src/backend/replication/
  24.6% src/backend/utils/activity/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a41453530a1..266379c780a 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -553,16 +553,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 1ab09655a70..c33185bd337 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1826,7 +1822,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1847,7 +1842,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1958,8 +1952,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1968,15 +1961,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2879,8 +2863,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2975,9 +2957,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2985,7 +2964,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -2996,21 +2974,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..feae2ae5f44 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
-- 
2.34.1


--lCc1MWfC7i2uqs8g
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v3-0003-Add-FLUSH_MIXED-support-and-implement-it-for-RELA.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread

* [PATCH v4 3/4] Remove useless calls to flush some stats
@ 2026-01-06 11:06 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 12+ messages in thread

From: Bertrand Drouvot @ 2026-01-06 11:06 UTC (permalink / raw)

Now that some stats can be flushed outside of transaction boundaries, remove
useless calls to report/flush some stats. Those calls were in place because
before commit <XXXX> stats were flushed only at transaction boundaries.

Note that:

- it reverts 039549d70f6 (it just keeps its tests)
- it can't be done for checkpointer and bgworker for example because they don't
have a flush callback to call
- it can't be done for auxiliary process (walsummarizer for example) because they
currently do not register the new timeout handler
---
 src/backend/replication/walreceiver.c        | 10 ------
 src/backend/replication/walsender.c          | 36 ++------------------
 src/backend/utils/activity/pgstat_relation.c | 13 -------
 src/test/recovery/t/001_stream_rep.pl        |  1 +
 src/test/subscription/t/001_rep_changes.pl   |  1 +
 5 files changed, 4 insertions(+), 57 deletions(-)
  69.9% src/backend/replication/
  22.8% src/backend/utils/activity/
   3.5% src/test/recovery/t/
   3.6% src/test/subscription/t/

diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 6970af3f3ff..dcbe3517b46 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -565,16 +565,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 					 */
 					bool		requestReply = false;
 
-					/*
-					 * Report pending statistics to the cumulative stats
-					 * system.  This location is useful for the report as it
-					 * is not within a tight loop in the WAL receiver, to
-					 * avoid bloating pgstats with requests, while also making
-					 * sure that the reports happen each time a status update
-					 * is sent.
-					 */
-					pgstat_report_wal(false);
-
 					/*
 					 * Check if time since last receive from primary has
 					 * reached the configured limit.
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0e6a3d200c..74102def9c7 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -94,14 +94,10 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
-#include "utils/pgstat_internal.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
-/* Minimum interval used by walsender for stats flushes, in ms */
-#define WALSENDER_STATS_FLUSH_INTERVAL         1000
-
 /*
  * Maximum data payload in a WAL data message.  Must be >= XLOG_BLCKSZ.
  *
@@ -1825,7 +1821,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	int			wakeEvents;
 	uint32		wait_event = 0;
 	static XLogRecPtr RecentFlushPtr = InvalidXLogRecPtr;
-	TimestampTz last_flush = 0;
 
 	/*
 	 * Fast path to avoid acquiring the spinlock in case we already know we
@@ -1846,7 +1841,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 	{
 		bool		wait_for_standby_at_stop = false;
 		long		sleeptime;
-		TimestampTz now;
 
 		/* Clear any already-pending wakeups */
 		ResetLatch(MyLatch);
@@ -1957,8 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		 * new WAL to be generated.  (But if we have nothing to send, we don't
 		 * want to wake on socket-writable.)
 		 */
-		now = GetCurrentTimestamp();
-		sleeptime = WalSndComputeSleeptime(now);
+		sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 		wakeEvents = WL_SOCKET_READABLE;
 
@@ -1967,15 +1960,6 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		Assert(wait_event != 0);
 
-		/* Report IO statistics, if needed */
-		if (TimestampDifferenceExceeds(last_flush, now,
-									   WALSENDER_STATS_FLUSH_INTERVAL))
-		{
-			pgstat_flush_io(false);
-			(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-			last_flush = now;
-		}
-
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
@@ -2878,8 +2862,6 @@ WalSndCheckTimeOut(void)
 static void
 WalSndLoop(WalSndSendDataCallback send_data)
 {
-	TimestampTz last_flush = 0;
-
 	/*
 	 * Initialize the last reply timestamp. That enables timeout processing
 	 * from hereon.
@@ -2974,9 +2956,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		 * WalSndWaitForWal() handle any other blocking; idle receivers need
 		 * its additional actions.  For physical replication, also block if
 		 * caught up; its send_data does not block.
-		 *
-		 * The IO statistics are reported in WalSndWaitForWal() for the
-		 * logical WAL senders.
 		 */
 		if ((WalSndCaughtUp && send_data != XLogSendLogical &&
 			 !streamingDoneSending) ||
@@ -2984,7 +2963,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		{
 			long		sleeptime;
 			int			wakeEvents;
-			TimestampTz now;
 
 			if (!streamingDoneReceiving)
 				wakeEvents = WL_SOCKET_READABLE;
@@ -2995,21 +2973,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
 			 * Use fresh timestamp, not last_processing, to reduce the chance
 			 * of reaching wal_sender_timeout before sending a keepalive.
 			 */
-			now = GetCurrentTimestamp();
-			sleeptime = WalSndComputeSleeptime(now);
+			sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp());
 
 			if (pq_is_send_pending())
 				wakeEvents |= WL_SOCKET_WRITEABLE;
 
-			/* Report IO statistics, if needed */
-			if (TimestampDifferenceExceeds(last_flush, now,
-										   WALSENDER_STATS_FLUSH_INTERVAL))
-			{
-				pgstat_flush_io(false);
-				(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
-				last_flush = now;
-			}
-
 			/* Sleep until something happens or we time out */
 			WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
 		}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..feae2ae5f44 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -260,15 +260,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/*
-	 * Flush IO statistics now. pgstat_report_stat() will flush IO stats,
-	 * however this will not be called until after an entire autovacuum cycle
-	 * is done -- which will likely vacuum many relations -- or until the
-	 * VACUUM command has processed all tables and committed.
-	 */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
@@ -360,10 +351,6 @@ pgstat_report_analyze(Relation rel,
 	}
 
 	pgstat_unlock_entry(entry_ref);
-
-	/* see pgstat_report_vacuum() */
-	pgstat_flush_io(false);
-	(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
 }
 
 /*
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index e9ac67813c7..c058a5f9b1f 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -15,6 +15,7 @@ my $node_primary = PostgreSQL::Test::Cluster->new('primary');
 $node_primary->init(
 	allows_streaming => 1,
 	auth_extra => [ '--create-role' => 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_primary->start;
 my $backup_name = 'my_backup';
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index d7e62e4d488..dda872f7074 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -11,6 +11,7 @@ use Test::More;
 # Initialize publisher node
 my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
 $node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf', "stats_flush_interval= '1s'");
 $node_publisher->start;
 
 # Create subscriber node
-- 
2.34.1


--wR/mWGXukst4NraF
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0004-Add-FLUSH_MIXED-support-and-implement-it-for-RELA.patch"



^ permalink  raw  reply  [nested|flat] 12+ messages in thread


end of thread, other threads:[~2026-01-06 11:06 UTC | newest]

Thread overview: 12+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-11-08 06:57 [PATCH v11 1/7] Row pattern recognition patch for raw parser. Tatsuo Ishii <[email protected]>
2026-01-06 11:06 [PATCH v5 3/4] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v6 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v7 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v9 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v10 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v11 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v1 2/3] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v8 4/5] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v2 2/3] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v3 2/3] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>
2026-01-06 11:06 [PATCH v4 3/4] Remove useless calls to flush some stats Bertrand Drouvot <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox