agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
[PATCH 2/5] Remove TLI from some argument lists.
15+ messages / 3 participants
[nested] [flat]

* [PATCH 2/5] Remove TLI from some argument lists.
@ 2019-07-09 09:54 Antonin Houska <ah@cybertec.at>
  0 siblings, 0 replies; 15+ messages in thread

From: Antonin Houska @ 2019-07-09 09:54 UTC (permalink / raw)

The timeline information is available to caller via XLogReaderState. Now that
XLogRead() is gonna be (sometimes) responsible for determining the TLI, it
would have to be added the (TimeLineID *) argument too, just to be consistent
with the current coding style. Since XLogRead() updates also other
position-specific fields of XLogReaderState, it seems simpler if we remove the
output argument from XLogPageReadCB and always report the TLI via
XLogReaderState.
---
 src/backend/access/transam/xlog.c              |  7 +++----
 src/backend/access/transam/xlogreader.c        |  6 +++---
 src/backend/access/transam/xlogutils.c         | 11 ++++++-----
 src/backend/replication/logical/logicalfuncs.c |  4 ++--
 src/backend/replication/walsender.c            |  2 +-
 src/bin/pg_rewind/parsexlog.c                  |  9 ++++-----
 src/bin/pg_waldump/pg_waldump.c                |  2 +-
 src/include/access/xlogreader.h                |  8 +++-----
 src/include/access/xlogutils.h                 |  5 ++---
 src/include/replication/logicalfuncs.h         |  2 +-
 10 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b6c9353cbd..f30c2ce0ce 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -885,8 +885,7 @@ static int	XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli,
 						 int source, bool notfoundOk);
 static int	XLogFileReadAnyTLI(XLogSegNo segno, int emode, int source);
 static int	XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-						 TimeLineID *readTLI);
+						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 										bool fetching_ckpt, XLogRecPtr tliRecPtr);
 static int	emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
@@ -11520,7 +11519,7 @@ CancelBackup(void)
  */
 static int
 XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen,
-			 XLogRecPtr targetRecPtr, char *readBuf, TimeLineID *readTLI)
+			 XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private =
 	(XLogPageReadPrivate *) xlogreader->private_data;
@@ -11637,7 +11636,7 @@ retry:
 	Assert(targetPageOff == readOff);
 	Assert(reqLen <= readLen);
 
-	*readTLI = curFileTLI;
+	xlogreader->readPageTLI = curFileTLI;
 
 	/*
 	 * Check the page header immediately, so that we can retry immediately if
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 08bc9695c1..ed7a72bc14 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -557,7 +557,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 
 		readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 
@@ -575,7 +575,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	 */
 	readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
 							   state->currRecPtr,
-							   state->readBuf, &state->readPageTLI);
+							   state->readBuf);
 	if (readLen < 0)
 		goto err;
 
@@ -594,7 +594,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	{
 		readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 	}
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663bae6..cba180912b 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -909,12 +909,12 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
  */
 int
 read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page,
-					 TimeLineID *pageTLI)
+					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	read_upto,
 				loc;
 	int			count;
+	TimeLineID	pageTLI;
 
 	loc = targetPagePtr + reqLen;
 
@@ -934,7 +934,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		else
 			read_upto = GetXLogReplayRecPtr(&ThisTimeLineID);
 
-		*pageTLI = ThisTimeLineID;
+		pageTLI = ThisTimeLineID;
 
 		/*
 		 * Check which timeline to get the record from.
@@ -991,7 +991,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 			 * nothing cares so long as the timeline doesn't go backwards.  We
 			 * should read the page header instead; FIXME someday.
 			 */
-			*pageTLI = state->currTLI;
+			pageTLI = state->currTLI;
 
 			/* No need to wait on a historical timeline */
 			break;
@@ -1022,8 +1022,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 	 * as 'count', read the whole page anyway. It's guaranteed to be
 	 * zero-padded up to the page boundary if it's incomplete.
 	 */
-	XLogRead(cur_page, state->wal_segment_size, *pageTLI, targetPagePtr,
+	XLogRead(cur_page, state->wal_segment_size, pageTLI, targetPagePtr,
 			 XLOG_BLCKSZ);
+	state->readPageTLI = pageTLI;
 
 	/* number of valid bytes in the buffer */
 	return count;
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index d974400d6e..d1cf80d441 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -116,10 +116,10 @@ check_permissions(void)
 
 int
 logical_read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	return read_local_xlog_page(state, targetPagePtr, reqLen,
-								targetRecPtr, cur_page, pageTLI);
+								targetRecPtr, cur_page);
 }
 
 /*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index e7a59b0a92..c8802bbc1f 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -763,7 +763,7 @@ StartReplication(StartReplicationCmd *cmd)
  */
 static int
 logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-					   XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+					   XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	flushptr;
 	int			count;
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 287af60c4e..00168c27dc 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -47,10 +47,10 @@ typedef struct XLogPageReadPrivate
 	int			tliIndex;
 } XLogPageReadPrivate;
 
+
 static int	SimpleXLogPageRead(XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
-							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-							   TimeLineID *pageTLI);
+							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 
 /*
  * Read WAL from the datadir/pg_wal, starting from 'startpoint' on timeline
@@ -238,8 +238,7 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
 /* XLogreader callback function, to read a WAL page */
 static int
 SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-				   TimeLineID *pageTLI)
+				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
 	uint32		targetPageOff;
@@ -322,7 +321,7 @@ SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
 
 	Assert(targetSegNo == xlogreadsegno);
 
-	*pageTLI = targetHistory[private->tliIndex].tli;
+	xlogreader->readPageTLI = targetHistory[private->tliIndex].tli;
 	return XLOG_BLCKSZ;
 }
 
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b95d467805..40c64a0bbf 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -423,7 +423,7 @@ XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
  */
 static int
 XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-				 XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
+				 XLogRecPtr targetPtr, char *readBuff)
 {
 	XLogDumpPrivate *private = state->private_data;
 	int			count = XLOG_BLCKSZ;
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index a3d3cc1e7b..e23223ba5b 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -34,8 +34,7 @@ typedef int (*XLogPageReadCB) (XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
 							   int reqLen,
 							   XLogRecPtr targetRecPtr,
-							   char *readBuf,
-							   TimeLineID *pageTLI);
+							   char *readBuf);
 
 typedef struct
 {
@@ -95,9 +94,8 @@ struct XLogReaderState
 	 * actual WAL record it's interested in.  In that case, targetRecPtr can
 	 * be used to determine which timeline to read the page from.
 	 *
-	 * The callback shall set *pageTLI to the TLI of the file the page was
-	 * read from.  It is currently used only for error reporting purposes, to
-	 * reconstruct the name of the WAL file where an error occurred.
+	 * The callback shall set ->readPageTLI to the TLI of the file the page
+	 * was read from.
 	 */
 	XLogPageReadCB read_page;
 
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59904..4fb305bafd 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -47,12 +47,11 @@ extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
+
 extern int	read_local_xlog_page(XLogReaderState *state,
 								 XLogRecPtr targetPagePtr, int reqLen,
-								 XLogRecPtr targetRecPtr, char *cur_page,
-								 TimeLineID *pageTLI);
+								 XLogRecPtr targetRecPtr, char *cur_page);
 
 extern void XLogReadDetermineTimeline(XLogReaderState *state,
 									  XLogRecPtr wantPage, uint32 wantLength);
-
 #endif
diff --git a/src/include/replication/logicalfuncs.h b/src/include/replication/logicalfuncs.h
index a9c178a9e6..012096f183 100644
--- a/src/include/replication/logicalfuncs.h
+++ b/src/include/replication/logicalfuncs.h
@@ -14,6 +14,6 @@
 extern int	logical_read_local_xlog_page(XLogReaderState *state,
 										 XLogRecPtr targetPagePtr,
 										 int reqLen, XLogRecPtr targetRecPtr,
-										 char *cur_page, TimeLineID *pageTLI);
+										 char *cur_page);
 
 #endif
-- 
2.16.4


--=-=-=
Content-Type: text/x-diff
Content-Disposition: attachment;
 filename=v04-0003-Introduce-XLogSegment-structure.patch



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

* [PATCH 1/4] Remove TLI from some argument lists.
@ 2019-09-09 09:53 Antonin Houska <ah@cybertec.at>
  0 siblings, 0 replies; 15+ messages in thread

From: Antonin Houska @ 2019-09-09 09:53 UTC (permalink / raw)

The timeline information is available to caller via XLogReaderState. Now that
XLogRead() is gonna be (sometimes) responsible for determining the TLI, it
would have to be added the (TimeLineID *) argument too, just to be consistent
with the current coding style. Since XLogRead() updates also other
position-specific fields of XLogReaderState, it seems simpler if we remove the
output argument from XLogPageReadCB and always report the TLI via
XLogReaderState.
---
 src/backend/access/transam/xlog.c              |  7 +++----
 src/backend/access/transam/xlogreader.c        |  6 +++---
 src/backend/access/transam/xlogutils.c         | 11 ++++++-----
 src/backend/replication/logical/logicalfuncs.c |  4 ++--
 src/backend/replication/walsender.c            |  2 +-
 src/bin/pg_rewind/parsexlog.c                  |  9 ++++-----
 src/bin/pg_waldump/pg_waldump.c                |  2 +-
 src/include/access/xlogreader.h                |  8 +++-----
 src/include/access/xlogutils.h                 |  5 ++---
 src/include/replication/logicalfuncs.h         |  2 +-
 10 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 6876537b62..cd948dbefc 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -885,8 +885,7 @@ static int	XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli,
 						 int source, bool notfoundOk);
 static int	XLogFileReadAnyTLI(XLogSegNo segno, int emode, int source);
 static int	XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-						 TimeLineID *readTLI);
+						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 										bool fetching_ckpt, XLogRecPtr tliRecPtr);
 static int	emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
@@ -11523,7 +11522,7 @@ CancelBackup(void)
  */
 static int
 XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen,
-			 XLogRecPtr targetRecPtr, char *readBuf, TimeLineID *readTLI)
+			 XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private =
 	(XLogPageReadPrivate *) xlogreader->private_data;
@@ -11640,7 +11639,7 @@ retry:
 	Assert(targetPageOff == readOff);
 	Assert(reqLen <= readLen);
 
-	*readTLI = curFileTLI;
+	xlogreader->readPageTLI = curFileTLI;
 
 	/*
 	 * Check the page header immediately, so that we can retry immediately if
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index a66e3324b1..2184f4291d 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -559,7 +559,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 
 		readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 
@@ -577,7 +577,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	 */
 	readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
 							   state->currRecPtr,
-							   state->readBuf, &state->readPageTLI);
+							   state->readBuf);
 	if (readLen < 0)
 		goto err;
 
@@ -596,7 +596,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	{
 		readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 	}
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 1fc39333f1..680bed8278 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -909,12 +909,12 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
  */
 int
 read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page,
-					 TimeLineID *pageTLI)
+					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	read_upto,
 				loc;
 	int			count;
+	TimeLineID	pageTLI;
 
 	loc = targetPagePtr + reqLen;
 
@@ -934,7 +934,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		else
 			read_upto = GetXLogReplayRecPtr(&ThisTimeLineID);
 
-		*pageTLI = ThisTimeLineID;
+		pageTLI = ThisTimeLineID;
 
 		/*
 		 * Check which timeline to get the record from.
@@ -991,7 +991,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 			 * nothing cares so long as the timeline doesn't go backwards.  We
 			 * should read the page header instead; FIXME someday.
 			 */
-			*pageTLI = state->currTLI;
+			pageTLI = state->currTLI;
 
 			/* No need to wait on a historical timeline */
 			break;
@@ -1022,8 +1022,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 	 * as 'count', read the whole page anyway. It's guaranteed to be
 	 * zero-padded up to the page boundary if it's incomplete.
 	 */
-	XLogRead(cur_page, state->wal_segment_size, *pageTLI, targetPagePtr,
+	XLogRead(cur_page, state->wal_segment_size, pageTLI, targetPagePtr,
 			 XLOG_BLCKSZ);
+	state->readPageTLI = pageTLI;
 
 	/* number of valid bytes in the buffer */
 	return count;
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index d974400d6e..d1cf80d441 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -116,10 +116,10 @@ check_permissions(void)
 
 int
 logical_read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	return read_local_xlog_page(state, targetPagePtr, reqLen,
-								targetRecPtr, cur_page, pageTLI);
+								targetRecPtr, cur_page);
 }
 
 /*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 23870a25a5..28d8c31af8 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -763,7 +763,7 @@ StartReplication(StartReplicationCmd *cmd)
  */
 static int
 logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-					   XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+					   XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	flushptr;
 	int			count;
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 63c3879ead..0a89f9c02a 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -47,10 +47,10 @@ typedef struct XLogPageReadPrivate
 	int			tliIndex;
 } XLogPageReadPrivate;
 
+
 static int	SimpleXLogPageRead(XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
-							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-							   TimeLineID *pageTLI);
+							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 
 /*
  * Read WAL from the datadir/pg_wal, starting from 'startpoint' on timeline
@@ -237,8 +237,7 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
 /* XLogReader callback function, to read a WAL page */
 static int
 SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-				   TimeLineID *pageTLI)
+				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
 	uint32		targetPageOff;
@@ -321,7 +320,7 @@ SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
 
 	Assert(targetSegNo == xlogreadsegno);
 
-	*pageTLI = targetHistory[private->tliIndex].tli;
+	xlogreader->readPageTLI = targetHistory[private->tliIndex].tli;
 	return XLOG_BLCKSZ;
 }
 
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b95d467805..40c64a0bbf 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -423,7 +423,7 @@ XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
  */
 static int
 XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-				 XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
+				 XLogRecPtr targetPtr, char *readBuff)
 {
 	XLogDumpPrivate *private = state->private_data;
 	int			count = XLOG_BLCKSZ;
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 735b1bd2fd..d64a9ad82f 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -38,8 +38,7 @@ typedef int (*XLogPageReadCB) (XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
 							   int reqLen,
 							   XLogRecPtr targetRecPtr,
-							   char *readBuf,
-							   TimeLineID *pageTLI);
+							   char *readBuf);
 
 typedef struct
 {
@@ -99,9 +98,8 @@ struct XLogReaderState
 	 * actual WAL record it's interested in.  In that case, targetRecPtr can
 	 * be used to determine which timeline to read the page from.
 	 *
-	 * The callback shall set *pageTLI to the TLI of the file the page was
-	 * read from.  It is currently used only for error reporting purposes, to
-	 * reconstruct the name of the WAL file where an error occurred.
+	 * The callback shall set ->readPageTLI to the TLI of the file the page
+	 * was read from.
 	 */
 	XLogPageReadCB read_page;
 
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59904..4fb305bafd 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -47,12 +47,11 @@ extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
+
 extern int	read_local_xlog_page(XLogReaderState *state,
 								 XLogRecPtr targetPagePtr, int reqLen,
-								 XLogRecPtr targetRecPtr, char *cur_page,
-								 TimeLineID *pageTLI);
+								 XLogRecPtr targetRecPtr, char *cur_page);
 
 extern void XLogReadDetermineTimeline(XLogReaderState *state,
 									  XLogRecPtr wantPage, uint32 wantLength);
-
 #endif
diff --git a/src/include/replication/logicalfuncs.h b/src/include/replication/logicalfuncs.h
index a9c178a9e6..012096f183 100644
--- a/src/include/replication/logicalfuncs.h
+++ b/src/include/replication/logicalfuncs.h
@@ -14,6 +14,6 @@
 extern int	logical_read_local_xlog_page(XLogReaderState *state,
 										 XLogRecPtr targetPagePtr,
 										 int reqLen, XLogRecPtr targetRecPtr,
-										 char *cur_page, TimeLineID *pageTLI);
+										 char *cur_page);
 
 #endif
-- 
2.22.0


--=-=-=
Content-Type: text/x-diff
Content-Disposition: attachment;
 filename=v05-0003-Introduce-XLogSegment-structure.patch



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

* [PATCH 2/6] Remove TLI from some argument lists.
@ 2019-09-23 05:40 Antonin Houska <ah@cybertec.at>
  0 siblings, 0 replies; 15+ messages in thread

From: Antonin Houska @ 2019-09-23 05:40 UTC (permalink / raw)

The timeline information is available to caller via XLogReaderState. Now that
XLogRead() is gonna be (sometimes) responsible for determining the TLI, it
would have to be added the (TimeLineID *) argument too, just to be consistent
with the current coding style. Since XLogRead() updates also other
position-specific fields of XLogReaderState, it seems simpler if we remove the
output argument from XLogPageReadCB and always report the TLI via
XLogReaderState.
---
 src/backend/access/transam/xlog.c              |  7 +++----
 src/backend/access/transam/xlogreader.c        |  6 +++---
 src/backend/access/transam/xlogutils.c         | 11 ++++++-----
 src/backend/replication/logical/logicalfuncs.c |  4 ++--
 src/backend/replication/walsender.c            |  2 +-
 src/bin/pg_rewind/parsexlog.c                  |  8 +++-----
 src/bin/pg_waldump/pg_waldump.c                |  2 +-
 src/include/access/xlogreader.h                |  8 +++-----
 src/include/access/xlogutils.h                 |  5 ++---
 src/include/replication/logicalfuncs.h         |  2 +-
 10 files changed, 25 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b7ff004234..7a89dfed7f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -885,8 +885,7 @@ static int	XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli,
 						 int source, bool notfoundOk);
 static int	XLogFileReadAnyTLI(XLogSegNo segno, int emode, int source);
 static int	XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-						 TimeLineID *readTLI);
+						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 										bool fetching_ckpt, XLogRecPtr tliRecPtr);
 static int	emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
@@ -11523,7 +11522,7 @@ CancelBackup(void)
  */
 static int
 XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen,
-			 XLogRecPtr targetRecPtr, char *readBuf, TimeLineID *readTLI)
+			 XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private =
 	(XLogPageReadPrivate *) xlogreader->private_data;
@@ -11640,7 +11639,7 @@ retry:
 	Assert(targetPageOff == readOff);
 	Assert(reqLen <= readLen);
 
-	*readTLI = curFileTLI;
+	xlogreader->readPageTLI = curFileTLI;
 
 	/*
 	 * Check the page header immediately, so that we can retry immediately if
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index a66e3324b1..2184f4291d 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -559,7 +559,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 
 		readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 
@@ -577,7 +577,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	 */
 	readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
 							   state->currRecPtr,
-							   state->readBuf, &state->readPageTLI);
+							   state->readBuf);
 	if (readLen < 0)
 		goto err;
 
@@ -596,7 +596,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 	{
 		readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
 								   state->currRecPtr,
-								   state->readBuf, &state->readPageTLI);
+								   state->readBuf);
 		if (readLen < 0)
 			goto err;
 	}
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 1fc39333f1..680bed8278 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -909,12 +909,12 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
  */
 int
 read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page,
-					 TimeLineID *pageTLI)
+					 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	read_upto,
 				loc;
 	int			count;
+	TimeLineID	pageTLI;
 
 	loc = targetPagePtr + reqLen;
 
@@ -934,7 +934,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		else
 			read_upto = GetXLogReplayRecPtr(&ThisTimeLineID);
 
-		*pageTLI = ThisTimeLineID;
+		pageTLI = ThisTimeLineID;
 
 		/*
 		 * Check which timeline to get the record from.
@@ -991,7 +991,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 			 * nothing cares so long as the timeline doesn't go backwards.  We
 			 * should read the page header instead; FIXME someday.
 			 */
-			*pageTLI = state->currTLI;
+			pageTLI = state->currTLI;
 
 			/* No need to wait on a historical timeline */
 			break;
@@ -1022,8 +1022,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 	 * as 'count', read the whole page anyway. It's guaranteed to be
 	 * zero-padded up to the page boundary if it's incomplete.
 	 */
-	XLogRead(cur_page, state->wal_segment_size, *pageTLI, targetPagePtr,
+	XLogRead(cur_page, state->wal_segment_size, pageTLI, targetPagePtr,
 			 XLOG_BLCKSZ);
+	state->readPageTLI = pageTLI;
 
 	/* number of valid bytes in the buffer */
 	return count;
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index d974400d6e..d1cf80d441 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -116,10 +116,10 @@ check_permissions(void)
 
 int
 logical_read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+							 int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
 {
 	return read_local_xlog_page(state, targetPagePtr, reqLen,
-								targetRecPtr, cur_page, pageTLI);
+								targetRecPtr, cur_page);
 }
 
 /*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 23870a25a5..28d8c31af8 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -763,7 +763,7 @@ StartReplication(StartReplicationCmd *cmd)
  */
 static int
 logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-					   XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
+					   XLogRecPtr targetRecPtr, char *cur_page)
 {
 	XLogRecPtr	flushptr;
 	int			count;
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 63c3879ead..33e2ba2a03 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -49,8 +49,7 @@ typedef struct XLogPageReadPrivate
 
 static int	SimpleXLogPageRead(XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
-							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-							   TimeLineID *pageTLI);
+							   int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 
 /*
  * Read WAL from the datadir/pg_wal, starting from 'startpoint' on timeline
@@ -237,8 +236,7 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
 /* XLogReader callback function, to read a WAL page */
 static int
 SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
-				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
-				   TimeLineID *pageTLI)
+				   int reqLen, XLogRecPtr targetRecPtr, char *readBuf)
 {
 	XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
 	uint32		targetPageOff;
@@ -321,7 +319,7 @@ SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
 
 	Assert(targetSegNo == xlogreadsegno);
 
-	*pageTLI = targetHistory[private->tliIndex].tli;
+	xlogreader->readPageTLI = targetHistory[private->tliIndex].tli;
 	return XLOG_BLCKSZ;
 }
 
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b95d467805..40c64a0bbf 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -423,7 +423,7 @@ XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
  */
 static int
 XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
-				 XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
+				 XLogRecPtr targetPtr, char *readBuff)
 {
 	XLogDumpPrivate *private = state->private_data;
 	int			count = XLOG_BLCKSZ;
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 735b1bd2fd..d64a9ad82f 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -38,8 +38,7 @@ typedef int (*XLogPageReadCB) (XLogReaderState *xlogreader,
 							   XLogRecPtr targetPagePtr,
 							   int reqLen,
 							   XLogRecPtr targetRecPtr,
-							   char *readBuf,
-							   TimeLineID *pageTLI);
+							   char *readBuf);
 
 typedef struct
 {
@@ -99,9 +98,8 @@ struct XLogReaderState
 	 * actual WAL record it's interested in.  In that case, targetRecPtr can
 	 * be used to determine which timeline to read the page from.
 	 *
-	 * The callback shall set *pageTLI to the TLI of the file the page was
-	 * read from.  It is currently used only for error reporting purposes, to
-	 * reconstruct the name of the WAL file where an error occurred.
+	 * The callback shall set ->readPageTLI to the TLI of the file the page
+	 * was read from.
 	 */
 	XLogPageReadCB read_page;
 
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59904..4fb305bafd 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -47,12 +47,11 @@ extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
+
 extern int	read_local_xlog_page(XLogReaderState *state,
 								 XLogRecPtr targetPagePtr, int reqLen,
-								 XLogRecPtr targetRecPtr, char *cur_page,
-								 TimeLineID *pageTLI);
+								 XLogRecPtr targetRecPtr, char *cur_page);
 
 extern void XLogReadDetermineTimeline(XLogReaderState *state,
 									  XLogRecPtr wantPage, uint32 wantLength);
-
 #endif
diff --git a/src/include/replication/logicalfuncs.h b/src/include/replication/logicalfuncs.h
index a9c178a9e6..012096f183 100644
--- a/src/include/replication/logicalfuncs.h
+++ b/src/include/replication/logicalfuncs.h
@@ -14,6 +14,6 @@
 extern int	logical_read_local_xlog_page(XLogReaderState *state,
 										 XLogRecPtr targetPagePtr,
 										 int reqLen, XLogRecPtr targetRecPtr,
-										 char *cur_page, TimeLineID *pageTLI);
+										 char *cur_page);
 
 #endif
-- 
2.20.1


--=-=-=
Content-Type: text/x-diff
Content-Disposition: attachment;
 filename=v06-0003-Introduce-WALOpenSegment-structure.patch



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

* [PATCH v7 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 16 ++++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 7 files changed, 66 insertions(+), 6 deletions(-)
  51.8% doc/src/sgml/
  13.3% src/backend/utils/activity/
  13.6% src/backend/utils/misc/
  11.3% src/include/
   9.8% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bc2690ce07..383bbe3a132 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8923,6 +8923,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index a4ff64dc5ce..dd85a27c52f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -123,6 +123,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -203,6 +205,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2164,6 +2167,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 271c033952e..d2734caafea 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b340a680614..ef856dbf55b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -35,9 +35,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 #define pgstat_schedule_anytime_update()												\
 	do {																				\
 		if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))		\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 	} while (0)
 
 extern void pgstat_reset_counters(void);
@@ -828,6 +825,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--C2xzVbxFmVrP7k6O
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v7-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v10 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 13 ++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/backend/utils/misc/timeout.c              |  6 ++++
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 src/include/utils/timeout.h                   |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 9 files changed, 70 insertions(+), 6 deletions(-)
  51.0% doc/src/sgml/
  10.6% src/backend/utils/activity/
  15.9% src/backend/utils/misc/
   3.6% src/include/utils/
   9.0% src/include/
   9.6% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20dbcaeb3ee..1eed71007a7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8929,6 +8929,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index ddd331e2c81..fd6ab0db16f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -124,6 +124,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -204,6 +206,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2171,6 +2174,16 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_all_timeouts_initialized())
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 9507778415d..073e08c7892 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ddba5dc607c..85c4260d1db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -828,3 +828,9 @@ get_timeout_finish_time(TimeoutId id)
 {
 	return all_timeouts[id].fin_time;
 }
+
+bool
+get_all_timeouts_initialized(void)
+{
+	return all_timeouts_initialized;
+}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b011a315679..90237c70829 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -34,9 +34,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 	do {																				\
 		if (IsUnderPostmaster && !pgstat_pending_anytime)								\
 		{																				\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 			pgstat_pending_anytime = true;												\
 		}																				\
 	} while (0)
@@ -831,6 +828,7 @@ extern PGDLLIMPORT bool pgstat_pending_anytime;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 10723bb664c..fe7327de209 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -93,5 +93,6 @@ extern bool get_timeout_active(TimeoutId id);
 extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 extern TimestampTz get_timeout_finish_time(TimeoutId id);
+extern bool get_all_timeouts_initialized(void);
 
 #endif							/* TIMEOUT_H */
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--NVvBxFuyV/R+1/8/
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v10-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v10 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 13 ++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/backend/utils/misc/timeout.c              |  6 ++++
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 src/include/utils/timeout.h                   |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 9 files changed, 70 insertions(+), 6 deletions(-)
  51.0% doc/src/sgml/
  10.6% src/backend/utils/activity/
  15.9% src/backend/utils/misc/
   3.6% src/include/utils/
   9.0% src/include/
   9.6% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20dbcaeb3ee..1eed71007a7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8929,6 +8929,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index ddd331e2c81..fd6ab0db16f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -124,6 +124,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -204,6 +206,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2171,6 +2174,16 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_all_timeouts_initialized())
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 9507778415d..073e08c7892 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ddba5dc607c..85c4260d1db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -828,3 +828,9 @@ get_timeout_finish_time(TimeoutId id)
 {
 	return all_timeouts[id].fin_time;
 }
+
+bool
+get_all_timeouts_initialized(void)
+{
+	return all_timeouts_initialized;
+}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b011a315679..90237c70829 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -34,9 +34,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 	do {																				\
 		if (IsUnderPostmaster && !pgstat_pending_anytime)								\
 		{																				\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 			pgstat_pending_anytime = true;												\
 		}																				\
 	} while (0)
@@ -831,6 +828,7 @@ extern PGDLLIMPORT bool pgstat_pending_anytime;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 10723bb664c..fe7327de209 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -93,5 +93,6 @@ extern bool get_timeout_active(TimeoutId id);
 extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 extern TimestampTz get_timeout_finish_time(TimeoutId id);
+extern bool get_all_timeouts_initialized(void);
 
 #endif							/* TIMEOUT_H */
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--NVvBxFuyV/R+1/8/
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v10-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v4 2/4] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  2 +-
 src/backend/tcop/postgres.c                   |  2 +-
 src/backend/utils/activity/pgstat.c           | 15 +++++++++
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  1 +
 src/include/utils/guc_hooks.h                 |  1 +
 9 files changed, 63 insertions(+), 3 deletions(-)
  55.5% doc/src/sgml/
   5.3% src/backend/storage/lmgr/
  12.6% src/backend/utils/activity/
   5.3% src/backend/utils/init/
  14.9% src/backend/utils/misc/
   3.9% src/include/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..3136816a933 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8834,6 +8834,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which non-transactional statistics are made visible
+        during running transactions. Non-transactional statistics include, for
+        example, WAL activity and I/O operations.
+        They become visible at that interval in monitoring views such as
+        <link linkend="monitoring-pg-stat-io-view"> <structname>pg_stat_io</structname></link>
+        and <link linkend="monitoring-pg-stat-wal-view"> <structname>pg_stat_wal</structname></link>
+        during running transactions.
+        If this value is specified without units, it is taken as milliseconds.
+        The default is 10 seconds (<literal>10s</literal>), which is probably
+        about the smallest value you would want in practice for long running
+        transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect transactional statistics such as
+         <structname>pg_stat_all_tables</structname> columns (like
+         <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         <structfield>n_tup_del</structfield>), which are always flushed at transaction
+         boundaries to maintain consistency.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 012705a2ee6..caa6eecca88 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1669,7 +1669,7 @@ ProcSleep(LOCALLOCK *locallock)
 	} while (myWaitStatus == PROC_WAIT_STATUS_WAITING);
 
 	if (anytime_timeout_was_active)
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 	/*
 	 * Disable the timers, if they are still running.  As in LockErrorCleanup,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 132fae61423..c0e81cb13d0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3543,7 +3543,7 @@ ProcessInterrupts(void)
 
 		/* Schedule next timeout */
 		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT,
-							 PGSTAT_MIN_INTERVAL);
+							 pgstat_flush_interval);
 	}
 
 	if (ProcSignalBarrierPending)
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index ab4d9088a9a..ca08dd49cd7 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -113,6 +113,7 @@
 #include "utils/memutils.h"
 #include "utils/pgstat_internal.h"
 #include "utils/timestamp.h"
+#include "utils/timeout.h"
 
 
 /* ----------
@@ -202,6 +203,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2165,6 +2167,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 6076f531c4a..c7c0d618671 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -768,7 +768,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
 						IdleStatsUpdateTimeoutHandler);
 		RegisterTimeout(ANYTIME_STATS_UPDATE_TIMEOUT,
 						AnytimeStatsUpdateTimeoutHandler);
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 	}
 
 	/*
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..3bb43362e51 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2782,6 +2782,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..6ce5a250170 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -669,6 +669,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1651f16f966..e0f222695bf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -816,6 +816,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..3a2ae6c41cd 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
-- 
2.34.1


--wR/mWGXukst4NraF
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0003-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v6 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 34 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 16 +++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 6 files changed, 64 insertions(+), 4 deletions(-)
  59.1% doc/src/sgml/
  14.2% src/backend/utils/activity/
  14.5% src/backend/utils/misc/
  12.0% src/include/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f1af1505cf3..20666679f90 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8871,6 +8871,40 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which statistics that can be updated while a
+        transaction is still running are made visible. These include, for example,
+        WAL activity and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>.
+        Other statistics are only made visible at transaction end and are not
+        affected by this setting.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 411b65aae3e..79eb59b5625 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -123,6 +123,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -203,6 +205,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2164,6 +2167,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index c1f1603cd39..fc0e4259b36 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2789,6 +2789,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 1ae594af843..3f998a0bea0 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -673,6 +673,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b340a680614..ef856dbf55b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -35,9 +35,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 #define pgstat_schedule_anytime_update()												\
 	do {																				\
 		if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))		\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 	} while (0)
 
 extern void pgstat_reset_counters(void);
@@ -828,6 +825,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..3a2ae6c41cd 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
-- 
2.34.1


--DxqFthphbM+ha9Dy
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v6-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v8 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 16 ++++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 7 files changed, 66 insertions(+), 6 deletions(-)
  51.8% doc/src/sgml/
  13.3% src/backend/utils/activity/
  13.6% src/backend/utils/misc/
  11.3% src/include/
   9.8% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index faf0bdb62aa..03875b490b7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8932,6 +8932,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index a4ff64dc5ce..dd85a27c52f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -123,6 +123,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -203,6 +205,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2164,6 +2167,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 271c033952e..d2734caafea 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b340a680614..ef856dbf55b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -35,9 +35,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 #define pgstat_schedule_anytime_update()												\
 	do {																				\
 		if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))		\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 	} while (0)
 
 extern void pgstat_reset_counters(void);
@@ -828,6 +825,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--OI5irqZsxWxBW9nT
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v11 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 13 ++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/backend/utils/misc/timeout.c              |  6 ++++
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 src/include/utils/timeout.h                   |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 9 files changed, 70 insertions(+), 6 deletions(-)
  51.0% doc/src/sgml/
  10.6% src/backend/utils/activity/
  15.9% src/backend/utils/misc/
   3.6% src/include/utils/
   9.0% src/include/
   9.6% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20dbcaeb3ee..1eed71007a7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8929,6 +8929,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index ddd331e2c81..fd6ab0db16f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -124,6 +124,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -204,6 +206,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2171,6 +2174,16 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_all_timeouts_initialized())
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 9507778415d..073e08c7892 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ddba5dc607c..85c4260d1db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -828,3 +828,9 @@ get_timeout_finish_time(TimeoutId id)
 {
 	return all_timeouts[id].fin_time;
 }
+
+bool
+get_all_timeouts_initialized(void)
+{
+	return all_timeouts_initialized;
+}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b011a315679..90237c70829 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -34,9 +34,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 	do {																				\
 		if (IsUnderPostmaster && !pgstat_pending_anytime)								\
 		{																				\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 			pgstat_pending_anytime = true;												\
 		}																				\
 	} while (0)
@@ -831,6 +828,7 @@ extern PGDLLIMPORT bool pgstat_pending_anytime;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 10723bb664c..fe7327de209 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -93,5 +93,6 @@ extern bool get_timeout_active(TimeoutId id);
 extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 extern TimestampTz get_timeout_finish_time(TimeoutId id);
+extern bool get_all_timeouts_initialized(void);
 
 #endif							/* TIMEOUT_H */
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 6ba4022418f..920443487c0 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,11 +164,12 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     SET LOCAL stats_fetch_consistency = none;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'fixed_anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -184,12 +185,13 @@ like($result, qr/^fixed_anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     SET LOCAL stats_fetch_consistency = none;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'var_anytime:'||calls from test_custom_stats_var_report('entry2');
-- 
2.34.1


--2MEBAGW8+kohXisi
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v11-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v11 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 13 ++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/backend/utils/misc/timeout.c              |  6 ++++
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 src/include/utils/timeout.h                   |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 9 files changed, 70 insertions(+), 6 deletions(-)
  51.0% doc/src/sgml/
  10.6% src/backend/utils/activity/
  15.9% src/backend/utils/misc/
   3.6% src/include/utils/
   9.0% src/include/
   9.6% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20dbcaeb3ee..1eed71007a7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8929,6 +8929,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index ddd331e2c81..fd6ab0db16f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -124,6 +124,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -204,6 +206,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2171,6 +2174,16 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_all_timeouts_initialized())
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 9507778415d..073e08c7892 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ddba5dc607c..85c4260d1db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -828,3 +828,9 @@ get_timeout_finish_time(TimeoutId id)
 {
 	return all_timeouts[id].fin_time;
 }
+
+bool
+get_all_timeouts_initialized(void)
+{
+	return all_timeouts_initialized;
+}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b011a315679..90237c70829 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -34,9 +34,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 	do {																				\
 		if (IsUnderPostmaster && !pgstat_pending_anytime)								\
 		{																				\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 			pgstat_pending_anytime = true;												\
 		}																				\
 	} while (0)
@@ -831,6 +828,7 @@ extern PGDLLIMPORT bool pgstat_pending_anytime;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 10723bb664c..fe7327de209 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -93,5 +93,6 @@ extern bool get_timeout_active(TimeoutId id);
 extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 extern TimestampTz get_timeout_finish_time(TimeoutId id);
+extern bool get_all_timeouts_initialized(void);
 
 #endif							/* TIMEOUT_H */
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 6ba4022418f..920443487c0 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,11 +164,12 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     SET LOCAL stats_fetch_consistency = none;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'fixed_anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -184,12 +185,13 @@ like($result, qr/^fixed_anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     SET LOCAL stats_fetch_consistency = none;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'var_anytime:'||calls from test_custom_stats_var_report('entry2');
-- 
2.34.1


--2MEBAGW8+kohXisi
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v11-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v5 2/4] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/access/transam/xlog.c             |  4 +--
 src/backend/utils/activity/pgstat.c           | 16 +++++++++-
 src/backend/utils/activity/pgstat_backend.c   |  4 +--
 src/backend/utils/activity/pgstat_io.c        |  2 +-
 src/backend/utils/activity/pgstat_slru.c      |  2 +-
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  1 +
 src/include/utils/guc_hooks.h                 |  1 +
 10 files changed, 66 insertions(+), 7 deletions(-)
  45.9% doc/src/sgml/
   6.5% src/backend/access/transam/
  31.8% src/backend/utils/activity/
  12.3% src/backend/utils/misc/
   3.2% src/include/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..3136816a933 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8834,6 +8834,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which non-transactional statistics are made visible
+        during running transactions. Non-transactional statistics include, for
+        example, WAL activity and I/O operations.
+        They become visible at that interval in monitoring views such as
+        <link linkend="monitoring-pg-stat-io-view"> <structname>pg_stat_io</structname></link>
+        and <link linkend="monitoring-pg-stat-wal-view"> <structname>pg_stat_wal</structname></link>
+        during running transactions.
+        If this value is specified without units, it is taken as milliseconds.
+        The default is 10 seconds (<literal>10s</literal>), which is probably
+        about the smallest value you would want in practice for long running
+        transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect transactional statistics such as
+         <structname>pg_stat_all_tables</structname> columns (like
+         <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         <structfield>n_tup_del</structfield>), which are always flushed at transaction
+         boundaries to maintain consistency.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 9503aea5b4d..31523dea923 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -1087,7 +1087,7 @@ XLogInsertRecord(XLogRecData *rdata,
 
 		/* Schedule next anytime stats update timeout */
 		if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 		/* Required for the flush of pending stats WAL data */
 		pgstat_report_fixed = true;
@@ -2073,7 +2073,7 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic)
 					/* Schedule next anytime stats update timeout */
 					if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
 						enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT,
-											 PGSTAT_MIN_INTERVAL);
+											 pgstat_flush_interval);
 
 					/*
 					 * Required for the flush of pending stats WAL data, per
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 2c9454677e9..dd174129403 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -203,6 +203,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -1304,7 +1305,7 @@ pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *creat
 		/* Schedule next anytime stats update timeout */
 		if (kind_info->flush_mode == FLUSH_ANYTIME && IsUnderPostmaster &&
 			!get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 	}
 
 	return entry_ref;
@@ -2172,6 +2173,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 9dcb24db975..f5b8c7b039c 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -69,7 +69,7 @@ pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
 
 	/* Schedule next anytime stats update timeout */
 	if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 	backend_has_iostats = true;
 	pgstat_report_fixed = true;
@@ -89,7 +89,7 @@ pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
 
 	/* Schedule next anytime stats update timeout */
 	if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 	backend_has_iostats = true;
 	pgstat_report_fixed = true;
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index 53dbf2a514b..b69a1e26f7d 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -82,7 +82,7 @@ pgstat_count_io_op(IOObject io_object, IOContext io_context, IOOp io_op,
 
 	/* Schedule next anytime stats update timeout */
 	if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 	have_iostats = true;
 	pgstat_report_fixed = true;
diff --git a/src/backend/utils/activity/pgstat_slru.c b/src/backend/utils/activity/pgstat_slru.c
index 1d16cde1889..36231ee874b 100644
--- a/src/backend/utils/activity/pgstat_slru.c
+++ b/src/backend/utils/activity/pgstat_slru.c
@@ -226,7 +226,7 @@ get_slru_entry(int slru_idx)
 
 	/* Schedule next anytime stats update timeout */
 	if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
-		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);
 
 	have_slrustats = true;
 	pgstat_report_fixed = true;
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..3bb43362e51 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2782,6 +2782,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..6ce5a250170 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -669,6 +669,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1651f16f966..e0f222695bf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -816,6 +816,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..3a2ae6c41cd 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
-- 
2.34.1


--WLtTrIUEfQDmERxy
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0003-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v7 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 16 ++++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 7 files changed, 66 insertions(+), 6 deletions(-)
  51.8% doc/src/sgml/
  13.3% src/backend/utils/activity/
  13.6% src/backend/utils/misc/
  11.3% src/include/
   9.8% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bc2690ce07..383bbe3a132 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8923,6 +8923,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index a4ff64dc5ce..dd85a27c52f 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -123,6 +123,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -203,6 +205,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2164,6 +2167,19 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))
+	{
+		disable_timeout(ANYTIME_STATS_UPDATE_TIMEOUT, false);
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+	}
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 271c033952e..d2734caafea 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index b340a680614..ef856dbf55b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -35,9 +35,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -548,7 +545,7 @@ extern void pgstat_force_next_flush(void);
 #define pgstat_schedule_anytime_update()												\
 	do {																				\
 		if (IsUnderPostmaster && !get_timeout_active(ANYTIME_STATS_UPDATE_TIMEOUT))		\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 	} while (0)
 
 extern void pgstat_reset_counters(void);
@@ -828,6 +825,7 @@ extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--C2xzVbxFmVrP7k6O
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v7-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH v9 3/5] Add GUC to specify non-transactional statistics flush interval
@ 2026-01-28 07:53 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 15+ messages in thread

From: Bertrand Drouvot @ 2026-01-28 07:53 UTC (permalink / raw)

Adding pgstat_flush_interval, a new GUC to set the interval between flushes of
non-transactional statistics.
---
 doc/src/sgml/config.sgml                      | 32 +++++++++++++++++++
 src/backend/utils/activity/pgstat.c           | 13 ++++++++
 src/backend/utils/misc/guc_parameters.dat     | 10 ++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/backend/utils/misc/timeout.c              |  6 ++++
 src/include/pgstat.h                          |  6 ++--
 src/include/utils/guc_hooks.h                 |  1 +
 src/include/utils/timeout.h                   |  1 +
 .../test_custom_stats/t/001_custom_stats.pl   |  6 ++--
 9 files changed, 70 insertions(+), 6 deletions(-)
  51.0% doc/src/sgml/
  10.6% src/backend/utils/activity/
  15.9% src/backend/utils/misc/
   3.6% src/include/utils/
   9.0% src/include/
   9.6% src/test/modules/test_custom_stats/t/

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index faf0bdb62aa..03875b490b7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8932,6 +8932,38 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-stats-flush-interval" xreflabel="stats_flush_interval">
+      <term><varname>stats_flush_interval</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>stats_flush_interval</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the interval at which certain statistics, which can be updated while a
+        transaction is in progress, are made visible. These include WAL activity
+        and I/O operations.
+        Such statistics are refreshed at the specified interval and can be observed
+        during active transactions in monitoring views such as
+        <link linkend="monitoring-pg-stat-wal-view"><structname>pg_stat_wal</structname></link>
+        and
+        <link linkend="monitoring-pg-stat-io-view"><structname>pg_stat_io</structname></link>.
+        If the value is specified without a unit, milliseconds are assumed.
+        The default is 10 seconds (<literal>10s</literal>), which is generally
+        the smallest practical value for long-running transactions.
+       </para>
+       <note>
+        <para>
+         This parameter does not affect statistics that are only reported at
+         transaction end, such as the columns of <structname>pg_stat_all_tables</structname>
+         (for example, <structfield>n_tup_ins</structfield>, <structfield>n_tup_upd</structfield>,
+         and <structfield>n_tup_del</structfield>). These statistics are always
+         flushed at the end of a transaction.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 419dc512d9b..578c575dbfb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -123,6 +123,8 @@
  * ----------
  */
 
+/* minimum interval non-forced stats flushes.*/
+#define PGSTAT_MIN_INTERVAL			1000
 /* how long until to block flushing pending stats updates */
 #define PGSTAT_MAX_INTERVAL			60000
 /* when to call pgstat_report_stat() again, even when idle */
@@ -203,6 +205,7 @@ static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
 
 bool		pgstat_track_counts = false;
 int			pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
+int			pgstat_flush_interval = 10000;
 
 
 /* ----------
@@ -2170,6 +2173,16 @@ assign_stats_fetch_consistency(int newval, void *extra)
 		force_stats_snapshot_clear = true;
 }
 
+/*
+ * GUC assign_hook for stats_flush_interval.
+ */
+void
+assign_stats_flush_interval(int newval, void *extra)
+{
+	if (get_all_timeouts_initialized())
+		enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, newval);
+}
+
 /*
  * Flushes only FLUSH_ANYTIME stats using non-blocking locks. Transactional
  * stats (FLUSH_AT_TXN_BOUNDARY) remain pending until transaction boundary.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 271c033952e..d2734caafea 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2801,6 +2801,16 @@
   assign_hook => 'assign_stats_fetch_consistency',
 },
 
+{ name => 'stats_flush_interval', type => 'int', context => 'PGC_USERSET', group => 'STATS_CUMULATIVE',
+  short_desc => 'Sets the interval between flushes of non-transactional statistics.',
+  flags => 'GUC_UNIT_MS',
+  variable => 'pgstat_flush_interval',
+  boot_val => '10000',
+  min => '1000',
+  max => 'INT_MAX',
+  assign_hook => 'assign_stats_flush_interval'
+},
+
 { name => 'subtransaction_buffers', type => 'int', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
   short_desc => 'Sets the size of the dedicated buffer pool used for the subtransaction cache.',
   long_desc => '0 means use a fraction of "shared_buffers".',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f938cc65a3a..8bd37a25b38 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -688,6 +688,7 @@
 #track_wal_io_timing = off
 #track_functions = none                 # none, pl, all
 #stats_fetch_consistency = cache        # cache, none, snapshot
+#stats_flush_interval = 10s             # in milliseconds
 
 
 # - Monitoring -
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ddba5dc607c..85c4260d1db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -828,3 +828,9 @@ get_timeout_finish_time(TimeoutId id)
 {
 	return all_timeouts[id].fin_time;
 }
+
+bool
+get_all_timeouts_initialized(void)
+{
+	return all_timeouts_initialized;
+}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f0f546d419a..7829c563316 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -35,9 +35,6 @@
 /* Default directory to store temporary statistics data in */
 #define PG_STAT_TMP_DIR		"pg_stat_tmp"
 
-/* Minimum interval non-forced stats flushes */
-#define PGSTAT_MIN_INTERVAL	1000
-
 /* Values for track_functions GUC variable --- order is significant! */
 typedef enum TrackFunctionsLevel
 {
@@ -549,7 +546,7 @@ extern void pgstat_force_next_flush(void);
 	do {																				\
 		if (IsUnderPostmaster && !pgstat_pending_anytime)								\
 		{																				\
-			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, PGSTAT_MIN_INTERVAL);	\
+			enable_timeout_after(ANYTIME_STATS_UPDATE_TIMEOUT, pgstat_flush_interval);	\
 			pgstat_pending_anytime = true;												\
 		}																				\
 	} while (0)
@@ -833,6 +830,7 @@ extern PGDLLIMPORT bool pgstat_pending_anytime;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
+extern PGDLLIMPORT int pgstat_flush_interval;
 
 
 /*
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 9c90670d9b8..9b5d2a90387 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -132,6 +132,7 @@ extern bool check_session_authorization(char **newval, void **extra, GucSource s
 extern void assign_session_authorization(const char *newval, void *extra);
 extern void assign_session_replication_role(int newval, void *extra);
 extern void assign_stats_fetch_consistency(int newval, void *extra);
+extern void assign_stats_flush_interval(int newval, void *extra);
 extern bool check_ssl(bool *newval, void **extra, GucSource source);
 extern bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 extern bool check_standard_conforming_strings(bool *newval, void **extra,
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 10723bb664c..fe7327de209 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -93,5 +93,6 @@ extern bool get_timeout_active(TimeoutId id);
 extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 extern TimestampTz get_timeout_finish_time(TimeoutId id);
+extern bool get_all_timeouts_initialized(void);
 
 #endif							/* TIMEOUT_H */
diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
index 7be1b281776..22e2a75dcb9 100644
--- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl
+++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl
@@ -164,10 +164,11 @@ $node->safe_psql('postgres', q(select test_custom_stats_fixed_reset()));
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 my $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_fixed_anytime_update() from generate_series(1, 2);
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
     select 'anytime:'||numcalls from test_custom_stats_fixed_report();
@@ -183,11 +184,12 @@ like($result, qr/^anytime:2/m,
 $node->safe_psql('postgres', q(select pg_stat_force_next_flush()));
 
 $anytime_test = q[
+    SET stats_flush_interval = '1s';
     BEGIN;
     -- Accumulate stats
     select test_custom_stats_var_anytime_update('entry2');
     select test_custom_stats_var_anytime_update('entry2');
-    -- Wait (has to be greater than PGSTAT_MIN_INTERVAL)
+    -- Wait (has to be greater than stats_flush_interval)
     select pg_sleep(1.5);
     -- Check
 	select * from test_custom_stats_var_report('entry2');
-- 
2.34.1


--aq/6bi8L6WORnI+9
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v9-0004-Remove-useless-calls-to-flush-some-stats.patch"



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

* [PATCH] Add hook for plugins to acquire sample rows during ANALYZE
@ 2026-05-29 12:35 Samba Siva <sambasivareddy.ch@zohomail.in>
  0 siblings, 0 replies; 15+ messages in thread

From: Samba Siva @ 2026-05-29 12:35 UTC (permalink / raw)

- Introduced AcquireSampleRowsFunc_hook for extensions to override row sampling.
- Updated analyze.c to utilize the hook if registered.
- Added tests to ensure ANALYZE completes without errors with the new hook.
---
 doc/src/sgml/xfunc.sgml               | 10 ++++++++++
 src/backend/commands/analyze.c        | 24 ++++++++++++++++++++----
 src/include/commands/vacuum.h         | 11 +++++++++++
 src/test/regress/expected/analyze.out | 20 ++++++++++++++++++++
 src/test/regress/parallel_schedule    |  1 +
 src/test/regress/sql/analyze.sql      | 24 ++++++++++++++++++++++++
 6 files changed, 86 insertions(+), 4 deletions(-)
 create mode 100644 src/test/regress/expected/analyze.out
 create mode 100644 src/test/regress/sql/analyze.sql

diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 1eb5abffd8..f1ef025ae2 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -4203,4 +4203,14 @@ supportfn(internal) returns internal
     To create such conditions, the support function must implement
     the <literal>SupportRequestIndexCondition</literal> request type.
    </para>
+
+   <para>
+    The hook variable <varname>AcquireSampleRowsFunc_hook</varname> allows extensions
+    to override the row sampling function during <command>ANALYZE</command> for regular
+    heap relations. This is useful for extensions/systems which implement distributed
+    databases and want to sample rows from remote nodes instead of the local heap.
+    The hook function fills the provided <literal>rows[]</literal> buffer with at most
+    <literal>targrows</literal> heap tuples and sets <literal>*totalrows</literal> to the
+    estimated total live row count of the relation.
+   </para>
   </sect1>
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 4fffb76e55..3560acdff1 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -74,6 +74,8 @@ int			default_statistics_target = 100;
 static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
 
+/* Hook for plugins to acquire sample rows for ANALYZE */
+AcquireSampleRowsFunc_hook_type AcquireSampleRowsFunc_hook = NULL;
 
 static void do_analyze_rel(Relation onerel,
 						   VacuumParams *params, List *va_cols,
@@ -188,8 +190,15 @@ analyze_rel(Oid relid, RangeVar *relation,
 	if (onerel->rd_rel->relkind == RELKIND_RELATION ||
 		onerel->rd_rel->relkind == RELKIND_MATVIEW)
 	{
-		/* Regular table, so we'll use the regular row acquisition function */
-		acquirefunc = acquire_sample_rows;
+		/*
+		 * Regular table, so we'll use the regular row acquisition function.
+		 * If a plugin has registered a hook to acquire sample rows, use it;
+		 * otherwise use the default function.
+		 */
+		if (AcquireSampleRowsFunc_hook)
+			acquirefunc = AcquireSampleRowsFunc_hook;
+		else
+			acquirefunc = acquire_sample_rows;
 		/* Also get regular table's size */
 		relpages = RelationGetNumberOfBlocks(onerel);
 	}
@@ -1467,8 +1476,15 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 		if (childrel->rd_rel->relkind == RELKIND_RELATION ||
 			childrel->rd_rel->relkind == RELKIND_MATVIEW)
 		{
-			/* Regular table, so use the regular row acquisition function */
-			acquirefunc = acquire_sample_rows;
+			/*
+			 * Regular table, so use the regular row acquisition function.
+			 * If a plugin has registered a hook to acquire sample rows, use it;
+			 * otherwise use the default function.
+			 */
+			if (AcquireSampleRowsFunc_hook)
+				acquirefunc = AcquireSampleRowsFunc_hook;
+			else
+				acquirefunc = acquire_sample_rows;
 			relpages = RelationGetNumberOfBlocks(childrel);
 		}
 		else if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index bc37a80dc7..146f936861 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -21,6 +21,7 @@
 #include "catalog/pg_class.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
+#include "foreign/fdwapi.h"
 #include "parser/parse_node.h"
 #include "storage/buf.h"
 #include "storage/lock.h"
@@ -113,6 +114,9 @@ typedef void (*AnalyzeAttrComputeStatsFunc) (VacAttrStatsP stats,
 											 int samplerows,
 											 double totalrows);
 
+/* Hook type for plugins to acquire sample rows for ANALYZE */
+typedef AcquireSampleRowsFunc AcquireSampleRowsFunc_hook_type;
+
 typedef struct VacAttrStats
 {
 	/*
@@ -334,6 +338,13 @@ extern PGDLLIMPORT int vacuum_cost_limit;
 
 extern PGDLLIMPORT int64 parallel_vacuum_worker_delay_ns;
 
+/*
+ * Hook for plugins to override row sampling during ANALYZE.
+ * Also applies to child relations of partitioned/inherited tables.
+ * See acquire_sample_rows() in src/backend/commands/analyze.c.
+ */
+extern PGDLLIMPORT AcquireSampleRowsFunc_hook_type AcquireSampleRowsFunc_hook;
+
 /* in commands/vacuum.c */
 extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
 extern void vacuum(List *relations, VacuumParams *params,
diff --git a/src/test/regress/expected/analyze.out b/src/test/regress/expected/analyze.out
new file mode 100644
index 0000000000..5bb6bbc45b
--- /dev/null
+++ b/src/test/regress/expected/analyze.out
@@ -0,0 +1,20 @@
+-- Test AcquireSampleRowsFunc_hook
+-- Usually this would be tested via a C extension.
+-- Here we just confirm this does not break the existing ANALYZE code
+-- by verifying that ANALYZE completes without error.
+CREATE TABLE employees (
+    id SERIAL PRIMARY KEY,
+    name TEXT,
+    department TEXT,
+    salary NUMERIC
+);
+INSERT INTO employees 
+    SELECT
+        i,
+        'Employee ' || i,
+        'Department ' || (i % 5),
+        (i % 100) * 1000 + 50000
+    FROM generate_series(1, 1000) i;
+-- Should complete without error
+ANALYZE employees;
+DROP TABLE employees;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e1e0c54019..71978ef858 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -94,6 +94,7 @@ test: vacuum_parallel
 # Run this alone, because concurrent DROP TABLE would make non-superuser
 # "ANALYZE;" fail with "relation with OID $n does not exist".
 test: maintain_every
+test: analyze
 
 # no relation related tests can be put in this group
 test: publication subscription
diff --git a/src/test/regress/sql/analyze.sql b/src/test/regress/sql/analyze.sql
new file mode 100644
index 0000000000..e0bc84e9f9
--- /dev/null
+++ b/src/test/regress/sql/analyze.sql
@@ -0,0 +1,24 @@
+-- Test AcquireSampleRowsFunc_hook
+-- Usually this would be tested via a C extension.
+-- Here we just confirm this does not break the existing ANALYZE code
+-- by verifying that ANALYZE completes without error.
+
+CREATE TABLE employees (
+    id SERIAL PRIMARY KEY,
+    name TEXT,
+    department TEXT,
+    salary NUMERIC
+);
+
+INSERT INTO employees 
+    SELECT
+        i,
+        'Employee ' || i,
+        'Department ' || (i % 5),
+        (i % 100) * 1000 + 50000
+    FROM generate_series(1, 1000) i;
+
+-- Should complete without error
+ANALYZE employees;
+
+DROP TABLE employees;
-- 
2.50.1 (Apple Git-155)


------=_Part_741589_445982825.1782438414800--






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


end of thread, other threads:[~2026-05-29 12:35 UTC | newest]

Thread overview: 15+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-07-09 09:54 [PATCH 2/5] Remove TLI from some argument lists. Antonin Houska <ah@cybertec.at>
2019-09-09 09:53 [PATCH 1/4] Remove TLI from some argument lists. Antonin Houska <ah@cybertec.at>
2019-09-23 05:40 [PATCH 2/6] Remove TLI from some argument lists. Antonin Houska <ah@cybertec.at>
2026-01-28 07:53 [PATCH v7 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v10 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v10 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v4 2/4] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v6 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v8 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v11 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v11 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v5 2/4] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v7 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-01-28 07:53 [PATCH v9 3/5] Add GUC to specify non-transactional statistics flush interval Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-05-29 12:35 [PATCH] Add hook for plugins to acquire sample rows during ANALYZE Samba Siva <sambasivareddy.ch@zohomail.in>

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