From 73dc4e9dded4f7b691bcb1e8171438ba5d7bb81a Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Thu, 24 Mar 2022 09:49:34 +0000
Subject: [PATCH v9] Add checkpoint stats of snapshot and mapping files of
 pg_logical dir

At times, there can be many snapshot and mapping files under
pg_logical dir that the checkpoint might have to delete/fsync
based on the cutoff LSN which can increase the checkpoint time.
Add stats related to these files to better understand the delays
or time spent by the checkpointer processing them.

Add these new log message only when necessary i.e. at least one
snapshot or mapping files is processed during the checkpoint.
---
 src/backend/access/heap/rewriteheap.c       |  4 +
 src/backend/access/transam/xlog.c           | 86 ++++++++++++++++-----
 src/backend/replication/logical/snapbuild.c |  2 +
 src/include/access/xlog.h                   | 11 +++
 4 files changed, 82 insertions(+), 21 deletions(-)

diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 2a53826736..696d04b5f0 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -1247,6 +1247,8 @@ CheckPointLogicalRewriteHeap(void)
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not remove file \"%s\": %m", path)));
+
+			CheckpointStats.repl_map_files_rmvd_cnt++;
 		}
 		else
 		{
@@ -1280,6 +1282,8 @@ CheckPointLogicalRewriteHeap(void)
 						(errcode_for_file_access(),
 						 errmsg("could not close file \"%s\": %m", path)));
 		}
+
+		CheckpointStats.repl_map_files_syncd_cnt++;
 	}
 	FreeDir(mappings_dir);
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 364f07bee9..a147f2d6fa 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6082,6 +6082,7 @@ LogCheckpointEnd(bool restartpoint)
 				longest_msecs,
 				average_msecs;
 	uint64		average_sync_time;
+	StringInfoData logmsg;
 
 	CheckpointStats.ckpt_end_t = GetCurrentTimestamp();
 
@@ -6117,27 +6118,63 @@ LogCheckpointEnd(bool restartpoint)
 			CheckpointStats.ckpt_sync_rels;
 	average_msecs = (long) ((average_sync_time + 999) / 1000);
 
-	ereport(LOG,
-	/* translator: the placeholders after first %s show restartpoint/checkpoint stats */
-			(errmsg("%s complete: wrote %d buffers (%.1f%%); "
-					"%d WAL file(s) added, %d removed, %d recycled; "
-					"write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; "
-					"sync files=%d, longest=%ld.%03d s, average=%ld.%03d s; "
-					"distance=%d kB, estimate=%d kB",
-					restartpoint ? _("restartpoint") : _("checkpoint"),
-					CheckpointStats.ckpt_bufs_written,
-					(double) CheckpointStats.ckpt_bufs_written * 100 / NBuffers,
-					CheckpointStats.ckpt_segs_added,
-					CheckpointStats.ckpt_segs_removed,
-					CheckpointStats.ckpt_segs_recycled,
-					write_msecs / 1000, (int) (write_msecs % 1000),
-					sync_msecs / 1000, (int) (sync_msecs % 1000),
-					total_msecs / 1000, (int) (total_msecs % 1000),
-					CheckpointStats.ckpt_sync_rels,
-					longest_msecs / 1000, (int) (longest_msecs % 1000),
-					average_msecs / 1000, (int) (average_msecs % 1000),
-					(int) (PrevCheckPointDistance / 1024.0),
-					(int) (CheckPointDistanceEstimate / 1024.0))));
+	initStringInfo(&logmsg);
+
+	if (restartpoint)
+		appendStringInfo(&logmsg, _("restartpoint complete: "));
+	else
+		appendStringInfo(&logmsg, _("checkpoint complete: "));
+
+	appendStringInfo(&logmsg,
+					_("wrote %d buffers (%.1f%%); "
+					  "%d WAL file(s) added, %d removed, %d recycled; "
+					  "write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; "
+					  "sync files=%d, longest=%ld.%03d s, average=%ld.%03d s; "
+					  "distance=%d kB, estimate=%d kB"),
+					  CheckpointStats.ckpt_bufs_written,
+					  (double) CheckpointStats.ckpt_bufs_written * 100 / NBuffers,
+					  CheckpointStats.ckpt_segs_added,
+					  CheckpointStats.ckpt_segs_removed,
+					  CheckpointStats.ckpt_segs_recycled,
+					  write_msecs / 1000, (int) (write_msecs % 1000),
+					  sync_msecs / 1000, (int) (sync_msecs % 1000),
+					  total_msecs / 1000, (int) (total_msecs % 1000),
+					  CheckpointStats.ckpt_sync_rels,
+					  longest_msecs / 1000, (int) (longest_msecs % 1000),
+					  average_msecs / 1000, (int) (average_msecs % 1000),
+					  (int) (PrevCheckPointDistance / 1024.0),
+					  (int) (CheckPointDistanceEstimate / 1024.0));
+
+	if (CheckpointStats.repl_snap_files_rmvd_cnt > 0)
+	{
+		long t_msecs;
+
+		t_msecs = TimestampDifferenceMilliseconds(CheckpointStats.repl_snap_start_t,
+												  CheckpointStats.repl_snap_end_t);
+
+		appendStringInfo(&logmsg,
+						_("; logical snapshot file(s) removed=%llu, time=%ld.%03d s"),
+						  (unsigned long long) CheckpointStats.repl_snap_files_rmvd_cnt,
+						  t_msecs / 1000, (int) (t_msecs % 1000));
+	}
+
+	if (CheckpointStats.repl_map_files_rmvd_cnt ||
+		CheckpointStats.repl_map_files_syncd_cnt > 0)
+	{
+		long t_msecs;
+
+		t_msecs = TimestampDifferenceMilliseconds(CheckpointStats.repl_snap_start_t,
+												  CheckpointStats.repl_snap_end_t);
+
+		appendStringInfo(&logmsg,
+						_("; logical rewrite mapping file(s) removed=%llu, synced=%llu, time=%ld.%03d s"),
+						  (unsigned long long) CheckpointStats.repl_map_files_rmvd_cnt,
+						  (unsigned long long) CheckpointStats.repl_map_files_syncd_cnt,
+						  t_msecs / 1000, (int) (t_msecs % 1000));
+	}
+
+	ereport(LOG, errmsg_internal("%s", logmsg.data));
+	pfree(logmsg.data);
 }
 
 /*
@@ -6794,8 +6831,15 @@ CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointRelationMap();
 	CheckPointReplicationSlots();
+
+	CheckpointStats.repl_snap_start_t = GetCurrentTimestamp();
 	CheckPointSnapBuild();
+	CheckpointStats.repl_snap_end_t = GetCurrentTimestamp();
+
+	CheckpointStats.repl_map_start_t = GetCurrentTimestamp();
 	CheckPointLogicalRewriteHeap();
+	CheckpointStats.repl_map_end_t = GetCurrentTimestamp();
+
 	CheckPointReplicationOrigin();
 
 	/* Write out all dirty data in SLRUs and the main buffer pool */
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index 83fca8a77d..2bfb367dc1 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -1997,6 +1997,8 @@ CheckPointSnapBuild(void)
 								path)));
 				continue;
 			}
+
+			CheckpointStats.repl_snap_files_rmvd_cnt++;
 		}
 	}
 	FreeDir(snap_dir);
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 09f6464331..a615724b4e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -172,6 +172,17 @@ typedef struct CheckpointStatsData
 									 * times, which is not necessarily the
 									 * same as the total elapsed time for the
 									 * entire sync phase. */
+
+	/* Statistics of snapshot files under "pg_logical/snapshots" */
+	uint64 repl_snap_files_rmvd_cnt;
+	TimestampTz	repl_snap_start_t;
+	TimestampTz	repl_snap_end_t;
+
+	/* Statistics of map files under "pg_logical/mappings" */
+	uint64 repl_map_files_syncd_cnt;
+	uint64 repl_map_files_rmvd_cnt;
+	TimestampTz	repl_map_start_t;
+	TimestampTz	repl_map_end_t;
 } CheckpointStatsData;
 
 extern CheckpointStatsData CheckpointStats;
-- 
2.25.1

