From 9e55ad5e1e1319cc32237e2ba79a097527d07fed Mon Sep 17 00:00:00 2001
From: Nitin Jadhav <nitinjadhav@microsoft.com>
Date: Wed, 23 Feb 2022 13:17:16 +0000
Subject: [PATCH] pg_stat_progress-checkpoint view

---
 doc/src/sgml/monitoring.sgml          | 357 ++++++++++++++++++++++++++
 src/backend/access/transam/xlog.c     | 132 ++++++++++
 src/backend/catalog/system_views.sql  |  36 +++
 src/backend/postmaster/checkpointer.c |  14 +-
 src/backend/storage/buffer/bufmgr.c   |   7 +
 src/backend/storage/sync/sync.c       |   6 +
 src/backend/utils/adt/pgstatfuncs.c   |  59 +++++
 src/include/access/xlog.h             |   4 +
 src/include/catalog/pg_proc.dat       |   9 +
 src/include/commands/progress.h       |  28 ++
 src/include/utils/backend_progress.h  |   3 +-
 src/test/regress/expected/rules.out   |  33 +++
 12 files changed, 684 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bf7625d988..1e7112eb78 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -401,6 +401,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
        See <xref linkend='copy-progress-reporting'/>.
       </entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_checkpoint</structname><indexterm><primary>pg_stat_progress_checkpoint</primary></indexterm></entry>
+      <entry>One row only, showing the progress of the <command>CHECKPOINT</command>.
+       See <xref linkend='checkpoint-progress-reporting'/>.
+      </entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -6895,6 +6902,356 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
   </table>
  </sect2>
 
+ <sect2 id="checkpoint-progress-reporting">
+  <title>CHECKPOINT Progress Reporting</title>
+
+  <indexterm>
+   <primary>pg_stat_progress_checkpoint</primary>
+  </indexterm>
+
+  <para>
+   Whenever the checkpoint operation is running, the
+   <structname>pg_stat_progress_checkpoint</structname> view will contain a
+   single row indicating the progress of the checkpoint. The tables below
+   describe the information that will be reported and provide information about
+   how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-checkpoint-view" xreflabel="pg_stat_progress_checkpoint">
+   <title><structname>pg_stat_progress_checkpoint</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>pid</structfield> <type>integer</type>
+      </para>
+      <para>
+       Process ID of a CHECKPOINTER process.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>type</structfield> <type>text</type>
+      </para>
+      <para>
+       Type of checkpoint. See <xref linkend="checkpoint-types"/>.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>kind</structfield> <type>text</type>
+      </para>
+      <para>
+       Kind of checkpoint. See <xref linkend="checkpoint-kinds"/>.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>start_lsn</structfield> <type>text</type>
+      </para>
+      <para>
+       The checkpoint start location.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>elapsed_time</structfield> <type>text</type>
+      </para>
+      <para>
+       Elapsed time of the checkpoint.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>phase</structfield> <type>text</type>
+      </para>
+      <para>
+       Current processing phase. See <xref linkend="checkpoint-phases"/>.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>buffers_total</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Total number of buffers to be written. This is estimated and reported
+       as of the beginning of buffer write operation.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>buffers_processed</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of buffers processed. This counter increases when the targeted
+       buffer is processed. This number will eventually become equal to
+       <literal> total_buffer_writes </literal> when the checkpoint is
+       complete.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>buffers_written</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of buffers written. This counter only advances when the targeted
+       buffers is written. Note that some of the buffers are processed but may
+       not required to be written. So this count will always be  less than or
+       equal to  <literal>total_buffer_writes</literal>.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>files_total</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Total number of files to be synced. This is estimated and reported as of
+       the beginning of sync operation.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>files_synced</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of files synced. This counter advances when the targeted file is
+       synced. This number will eventually become equal to
+       <literal>total_file_syncs</literal>  when the checkpoint is complete.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <table id="checkpoint-types">
+   <title>CHECKPOINT types</title>
+   <tgroup cols="2">
+    <colspec colname="col1" colwidth="1*"/>
+    <colspec colname="col2" colwidth="2*"/>
+    <thead>
+     <row>
+      <entry>Types</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>checkpoint</literal></entry>
+      <entry>
+       The current operation is checkpoint.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>restartpoint</literal></entry>
+      <entry>
+       The current operation is restartpoint.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <table id="checkpoint-kinds">
+   <title>CHECKPOINT kinds</title>
+   <tgroup cols="2">
+    <colspec colname="col1" colwidth="1*"/>
+    <colspec colname="col2" colwidth="2*"/>
+    <thead>
+     <row>
+      <entry>Kinds</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>shutdown</literal></entry>
+      <entry>
+       The checkpoint is for shutdown.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>end-of-recovery</literal></entry>
+      <entry>
+       The checkpoint is for end-of-recovery.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>immediate</literal></entry>
+      <entry>
+       The checkpoint is happens without delays.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>force</literal></entry>
+      <entry>
+       The checkpoint is started because some operation forced a checkpoint.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>flush all</literal></entry>
+      <entry>
+       The checkpoint flushes all pages, including those belonging to unlogged
+       tables.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>wait</literal></entry>
+      <entry>
+       Wait for completion before returning.
+      </entry>
+     </row>
+      <row>
+      <entry><literal>requested</literal></entry>
+      <entry>
+       The checkpoint request has been made.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>wal</literal></entry>
+      <entry>
+       The checkpoint is started because <literal>max_wal_size</literal> is
+       reached.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>time</literal></entry>
+      <entry>
+       The checkpoint is started because <literal>checkpoint_timeout</literal>
+       expired.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <table id="checkpoint-phases">
+   <title>CHECKPOINT phases</title>
+   <tgroup cols="2">
+    <colspec colname="col1" colwidth="1*"/>
+    <colspec colname="col2" colwidth="2*"/>
+    <thead>
+     <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>initializing</literal></entry>
+      <entry>
+       The CHECKPOINTER process is preparing to begin the checkpoint operation.
+       This phase is expected to be very brief.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing replication slots</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently flushing all the replication slots
+       to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing snapshots</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently removing all the serialized
+       snapshots that are not required anymore.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing logical rewrite mappings</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently removing/flushing the logical
+       rewrite mappings.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing CLOG pages</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing CLOG pages to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing Commit time stamp pages</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing Commit time stamp pages to
+       disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing SUBTRANS pages</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing SUBTRANS pages to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing MULTIXACT pages</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing MULTIXACT pages to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing SLRU pages</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing SLRU pages to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>checkpointing buffers</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently writing buffers to disk.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>performing sync requests</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently performing sync requests.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>performing two phase checkpoint</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently performing two phase checkpoint.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>recycling old XLOG files</literal></entry>
+      <entry>
+       The CHECKPOINTER process is currently recycling old XLOG files.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>finalizing</literal></entry>
+      <entry>
+       The CHECKPOINTER process is finalizing the checkpoint operation. 
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect2>
+
  </sect1>
 
  <sect1 id="dynamic-trace">
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0d2bd7a357..e5a19f3cc3 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -65,6 +65,7 @@
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_database.h"
+#include "commands/progress.h"
 #include "common/controldata_utils.h"
 #include "executor/instrument.h"
 #include "miscadmin.h"
@@ -653,6 +654,9 @@ static bool updateMinRecoveryPoint = true;
 static int	MyLockNo = 0;
 static bool holdingAllLocks = false;
 
+/* Copy of checkpoint flags. */
+static int ckpt_flags = 0;
+
 #ifdef WAL_DEBUG
 static MemoryContext walDebugCxt = NULL;
 #endif
@@ -6296,6 +6300,9 @@ CreateCheckPoint(int flags)
 	MemSet(&CheckpointStats, 0, sizeof(CheckpointStats));
 	CheckpointStats.ckpt_start_t = GetCurrentTimestamp();
 
+	/* Prepare to report progress of the checkpoint. */
+	checkpoint_progress_start(flags);
+
 	/*
 	 * Use a critical section to force system panic if we have trouble.
 	 */
@@ -6394,6 +6401,7 @@ CreateCheckPoint(int flags)
 			curInsert += SizeOfXLogShortPHD;
 	}
 	checkPoint.redo = curInsert;
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_LSN, checkPoint.redo);
 
 	/*
 	 * Here we update the shared RedoRecPtr for future XLogInsert calls; this
@@ -6629,8 +6637,12 @@ CreateCheckPoint(int flags)
 		KeepLogSeg(recptr, &_logSegNo);
 	}
 	_logSegNo--;
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_OLD_XLOG_RECYCLE);
 	RemoveOldXlogFiles(_logSegNo, RedoRecPtr, recptr,
 					   checkPoint.ThisTimeLineID);
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_FINALIZE);
 
 	/*
 	 * Make more log segments if needed.  (Do this after recycling old log
@@ -6652,6 +6664,9 @@ CreateCheckPoint(int flags)
 	/* Real work is done; log and update stats. */
 	LogCheckpointEnd(false);
 
+	/* Stop reporting progress of the checkpoint. */
+	checkpoint_progress_end();
+
 	/* Reset the process title */
 	update_checkpoint_display(flags, false, true);
 
@@ -6808,29 +6823,60 @@ static void
 CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointRelationMap();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_REPLI_SLOTS);
 	CheckPointReplicationSlots();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_SNAPSHOTS);
 	CheckPointSnapBuild();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_LOGICAL_REWRITE_MAPPINGS);
 	CheckPointLogicalRewriteHeap();
 	CheckPointReplicationOrigin();
 
 	/* Write out all dirty data in SLRUs and the main buffer pool */
 	TRACE_POSTGRESQL_BUFFER_CHECKPOINT_START(flags);
 	CheckpointStats.ckpt_write_t = GetCurrentTimestamp();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_CLOG_PAGES);
 	CheckPointCLOG();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_COMMITTS_PAGES);
 	CheckPointCommitTs();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_SUBTRANS_PAGES);
 	CheckPointSUBTRANS();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_MULTIXACT_PAGES);
 	CheckPointMultiXact();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_SLRU_PAGES);
 	CheckPointPredicate();
+
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_BUFFERS);
 	CheckPointBuffers(flags);
 
 	/* Perform all queued up fsyncs */
 	TRACE_POSTGRESQL_BUFFER_CHECKPOINT_SYNC_START();
 	CheckpointStats.ckpt_sync_t = GetCurrentTimestamp();
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_FILE_SYNC);
 	ProcessSyncRequests();
 	CheckpointStats.ckpt_sync_end_t = GetCurrentTimestamp();
 	TRACE_POSTGRESQL_BUFFER_CHECKPOINT_DONE();
 
 	/* We deliberately delay 2PC checkpointing as long as possible */
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_TWO_PHASE);
 	CheckPointTwoPhase(checkPointRedo);
 }
 
@@ -6977,6 +7023,9 @@ CreateRestartPoint(int flags)
 	MemSet(&CheckpointStats, 0, sizeof(CheckpointStats));
 	CheckpointStats.ckpt_start_t = GetCurrentTimestamp();
 
+	/* Prepare to report progress of the restartpoint. */
+	checkpoint_progress_start(flags);
+
 	if (log_checkpoints)
 		LogCheckpointStart(flags, true);
 
@@ -7077,7 +7126,11 @@ CreateRestartPoint(int flags)
 	if (!RecoveryInProgress())
 		replayTLI = XLogCtl->InsertTimeLineID;
 
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_OLD_XLOG_RECYCLE);
 	RemoveOldXlogFiles(_logSegNo, RedoRecPtr, endptr, replayTLI);
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_PHASE,
+									 PROGRESS_CHECKPOINT_PHASE_FINALIZE);
 
 	/*
 	 * Make more log segments if needed.  (Do this after recycling old log
@@ -7098,6 +7151,9 @@ CreateRestartPoint(int flags)
 	/* Real work is done; log and update stats. */
 	LogCheckpointEnd(true);
 
+	/* Stop reporting progress of the restartpoint. */
+	checkpoint_progress_end();
+
 	/* Reset the process title */
 	update_checkpoint_display(flags, true, true);
 
@@ -9197,3 +9253,79 @@ SetWalWriterSleeping(bool sleeping)
 	XLogCtl->WalWriterSleeping = sleeping;
 	SpinLockRelease(&XLogCtl->info_lck);
 }
+
+/*
+ * Start reporting progress of the checkpoint.
+ */
+void
+checkpoint_progress_start(int flags)
+{
+	/* In bootstrap mode, we don't actually record anything. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	ckpt_flags = flags;
+
+	/*
+	 * Cannot access pg_stat_progress_checkpoint view in case of checkpoint
+	 * during shutdown and end-of-recovery.
+	 */
+	if ((ckpt_flags &
+		 (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY)) == 0)
+	{
+		const int	index[] = {
+			PROGRESS_CHECKPOINT_TIMELINE,
+			PROGRESS_CHECKPOINT_KIND,
+			PROGRESS_CHECKPOINT_PHASE,
+			PROGRESS_CHECKPOINT_START_TIMESTAMP
+			};
+		int64		val[4];
+
+		pgstat_progress_start_command(PROGRESS_COMMAND_CHECKPOINT, InvalidOid);
+
+		val[0] = XLogCtl->InsertTimeLineID;
+		val[1] = flags;
+		val[2] = PROGRESS_CHECKPOINT_PHASE_INIT;
+		val[3] = CheckpointStats.ckpt_start_t;
+
+		pgstat_progress_update_multi_param(4, index, val);
+	}
+}
+
+/*
+ * Update index'th member in st_progress_param[] array with the latest value.
+ */
+void
+checkpoint_progress_update_param(int index, int64 val)
+{
+	/* In bootstrap mode, we don't actually record anything. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/*
+	 * Cannot access pg_stat_progress_checkpoint view in case of checkpoint
+	 * during shutdown and end-of-recovery.
+	 */
+	if ((ckpt_flags &
+		 (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY)) == 0)
+		pgstat_progress_update_param(index, val);
+}
+
+/*
+ * Stop reporting progress of the checkpoint.
+ */
+void
+checkpoint_progress_end(void)
+{
+	/* In bootstrap mode, we don't actually record anything. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/*
+	 * Cannot access pg_stat_progress_checkpoint view in case of checkpoint
+	 * during shutdown and end-of-recovery.
+	 */
+	if ((ckpt_flags &
+		 (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY)) == 0)
+		pgstat_progress_end_command();
+}
\ No newline at end of file
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3cb69b1f87..6dc5b0feb6 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1286,3 +1286,39 @@ CREATE VIEW pg_stat_subscription_workers AS
           FROM pg_subscription_rel) sr,
           LATERAL pg_stat_get_subscription_worker(sr.subid, sr.relid) w
           JOIN pg_subscription s ON (w.subid = s.oid);
+
+CREATE VIEW pg_stat_progress_checkpoint AS
+    SELECT
+        S.pid AS pid,
+        pg_stat_get_progress_checkpoint_type(S.param1) AS type,
+        pg_stat_get_progress_checkpoint_kind(S.param2) AS kind,
+        ( SELECT '0/0'::pg_lsn +
+                 ((CASE
+                     WHEN stat.lsn_int64 < 0 THEN pow(2::numeric, 64::numeric)::numeric
+                     ELSE 0::numeric
+                  END) +
+                  stat.lsn_int64::numeric)
+          FROM (SELECT s.param3::bigint) AS stat(lsn_int64)
+        ) AS start_lsn,
+        pg_stat_get_progress_checkpoint_elapsed(S.param4) AS elapsed_time,
+        CASE S.param5 WHEN 0 THEN 'initializing'
+                      WHEN 1 THEN 'checkpointing replication slots'
+                      WHEN 2 THEN 'checkpointing snapshots'
+                      WHEN 3 THEN 'checkpointing logical rewrite mappings'
+                      WHEN 4 THEN 'checkpointing CLOG pages'
+                      WHEN 5 THEN 'checkpointing CommitTs pages'
+                      WHEN 6 THEN 'checkpointing SUBTRANS pages'
+                      WHEN 7 THEN 'checkpointing MULTIXACT pages'
+                      WHEN 8 THEN 'checkpointing SLRU pages'
+                      WHEN 9 THEN 'checkpointing buffers'
+                      WHEN 10 THEN 'performing sync requests'
+                      WHEN 11 THEN 'performing two phase checkpoint'
+                      WHEN 12 THEN 'recycling old XLOG files'
+                      WHEN 13 THEN 'Finalizing'
+                      END AS phase,
+        S.param6 AS total_buffer_writes,
+        S.param7 AS buffers_processed,
+        S.param8 AS buffers_written,
+        S.param9 AS total_file_syncs,
+        S.param10 AS files_synced
+    FROM pg_stat_get_progress_info('CHECKPOINT') AS S;
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 4488e3a443..2ff7e77b3e 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -39,6 +39,7 @@
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
+#include "commands/progress.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -163,7 +164,7 @@ static pg_time_t last_xlog_switch_time;
 static void HandleCheckpointerInterrupts(void);
 static void CheckArchiveTimeout(void);
 static bool IsCheckpointOnSchedule(double progress);
-static bool ImmediateCheckpointRequested(void);
+static bool ImmediateCheckpointRequested(int flags);
 static bool CompactCheckpointerRequestQueue(void);
 static void UpdateSharedMemoryConfig(void);
 
@@ -658,16 +659,23 @@ CheckArchiveTimeout(void)
  * there is one pending behind it.)
  */
 static bool
-ImmediateCheckpointRequested(void)
+ImmediateCheckpointRequested(int flags)
 {
 	volatile CheckpointerShmemStruct *cps = CheckpointerShmem;
+	int		 updated_flags = flags;
 
 	/*
 	 * We don't need to acquire the ckpt_lck in this case because we're only
 	 * looking at a single flag bit.
 	 */
 	if (cps->ckpt_flags & CHECKPOINT_IMMEDIATE)
+	{
+		updated_flags |= CHECKPOINT_IMMEDIATE;
+		checkpoint_progress_update_param(PROGRESS_CHECKPOINT_KIND,
+										 updated_flags);
 		return true;
+	}
+
 	return false;
 }
 
@@ -699,7 +707,7 @@ CheckpointWriteDelay(int flags, double progress)
 	 */
 	if (!(flags & CHECKPOINT_IMMEDIATE) &&
 		!ShutdownRequestPending &&
-		!ImmediateCheckpointRequested() &&
+		!ImmediateCheckpointRequested(flags) &&
 		IsCheckpointOnSchedule(progress))
 	{
 		if (ConfigReloadPending)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index f5459c68f8..156130ef43 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -38,6 +38,7 @@
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
 #include "catalog/storage.h"
+#include "commands/progress.h"
 #include "executor/instrument.h"
 #include "lib/binaryheap.h"
 #include "miscadmin.h"
@@ -2012,6 +2013,8 @@ BufferSync(int flags)
 	WritebackContextInit(&wb_context, &checkpoint_flush_after);
 
 	TRACE_POSTGRESQL_BUFFER_SYNC_START(NBuffers, num_to_scan);
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_BUFFERS_TOTAL,
+									 num_to_scan);
 
 	/*
 	 * Sort buffers that need to be written to reduce the likelihood of random
@@ -2129,6 +2132,8 @@ BufferSync(int flags)
 		bufHdr = GetBufferDescriptor(buf_id);
 
 		num_processed++;
+		checkpoint_progress_update_param(PROGRESS_CHECKPOINT_BUFFERS_PROCESSED,
+										 num_processed);
 
 		/*
 		 * We don't need to acquire the lock here, because we're only looking
@@ -2149,6 +2154,8 @@ BufferSync(int flags)
 				TRACE_POSTGRESQL_BUFFER_SYNC_WRITTEN(buf_id);
 				PendingCheckpointerStats.m_buf_written_checkpoints++;
 				num_written++;
+				checkpoint_progress_update_param(PROGRESS_CHECKPOINT_BUFFERS_WRITTEN,
+												 num_written);
 			}
 		}
 
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index e161d57761..f0441d4de8 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -23,6 +23,7 @@
 #include "access/multixact.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
+#include "commands/progress.h"
 #include "commands/tablespace.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -356,6 +357,9 @@ ProcessSyncRequests(void)
 	/* Now scan the hashtable for fsync requests to process */
 	absorb_counter = FSYNCS_PER_ABSORB;
 	hash_seq_init(&hstat, pendingOps);
+	checkpoint_progress_update_param(PROGRESS_CHECKPOINT_FILES_TOTAL,
+									 hash_get_num_entries(pendingOps));
+
 	while ((entry = (PendingFsyncEntry *) hash_seq_search(&hstat)) != NULL)
 	{
 		int			failures;
@@ -419,6 +423,8 @@ ProcessSyncRequests(void)
 						longest = elapsed;
 					total_elapsed += elapsed;
 					processed++;
+					checkpoint_progress_update_param(PROGRESS_CHECKPOINT_FILES_SYNCED,
+													 processed);
 
 					if (log_checkpoints)
 						elog(DEBUG1, "checkpoint sync: number=%d file=%s time=%.3f ms",
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 30e8dfa7c1..32c35f0bd0 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -494,6 +494,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 		cmdtype = PROGRESS_COMMAND_BASEBACKUP;
 	else if (pg_strcasecmp(cmd, "COPY") == 0)
 		cmdtype = PROGRESS_COMMAND_COPY;
+	 else if (pg_strcasecmp(cmd, "CHECKPOINT") == 0)
+		cmdtype = PROGRESS_COMMAND_CHECKPOINT;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2495,3 +2497,60 @@ pg_stat_get_subscription_worker(PG_FUNCTION_ARGS)
 	/* Returns the record as Datum */
 	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
+
+/*
+ * Return checkpoint type (either checkpoint or restartpoint).
+ */
+Datum
+pg_stat_get_progress_checkpoint_type(PG_FUNCTION_ARGS)
+{
+	TimeLineID	cur_timeline = (TimeLineID) PG_GETARG_INT64(0);
+
+	if (RecoveryInProgress() || (GetWALInsertionTimeLine() != cur_timeline))
+		PG_RETURN_TEXT_P(CStringGetTextDatum("restartpoint"));
+	else
+		PG_RETURN_TEXT_P(CStringGetTextDatum("checkpoint"));
+}
+
+/*
+ * Return checkpoint kind based on the flags set.
+ */
+Datum
+pg_stat_get_progress_checkpoint_kind(PG_FUNCTION_ARGS)
+{
+	int64	flags = PG_GETARG_INT64(0);
+	char	ckpt_kind[MAXPGPATH];
+
+	MemSet(ckpt_kind, 0, MAXPGPATH);
+	snprintf(ckpt_kind, MAXPGPATH, "%s%s%s%s%s%s%s%s%s",
+			 (flags == 0) ? "unknown" : "",
+			 (flags & CHECKPOINT_IS_SHUTDOWN) ? "shutdown " : "",
+			 (flags & CHECKPOINT_END_OF_RECOVERY) ? "end-of-recovery " : "",
+			 (flags & CHECKPOINT_IMMEDIATE) ? "immediate " : "",
+			 (flags & CHECKPOINT_FORCE) ? "force " : "",
+			 (flags & CHECKPOINT_WAIT) ? "wait " : "",
+			 (flags & CHECKPOINT_CAUSE_XLOG) ? "wal " : "",
+			 (flags & CHECKPOINT_CAUSE_TIME) ? "time " : "",
+			 (flags & CHECKPOINT_FLUSH_ALL) ? "flush-all" : "");
+
+	PG_RETURN_TEXT_P(CStringGetTextDatum(ckpt_kind));
+}
+
+/*
+ * Return elapsed time (in seconds) of the checkpoint.
+ */
+Datum
+pg_stat_get_progress_checkpoint_elapsed(PG_FUNCTION_ARGS)
+{
+	TimestampTz		start = PG_GETARG_INT64(0);
+	TimestampTz		now = GetCurrentTimestamp();
+	char			elapsed_time[NAMEDATALEN];
+	long    		secs;
+	int     		usecs;
+
+	TimestampDifference(start, now, &secs, &usecs);
+	snprintf(elapsed_time, sizeof(elapsed_time), "%ld.%02d s", secs,
+			 (usecs / 10000));
+
+	PG_RETURN_TEXT_P(CStringGetTextDatum(elapsed_time));
+}
\ No newline at end of file
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 4b45ac64db..c0a3f57689 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -288,6 +288,10 @@ extern void do_pg_abort_backup(int code, Datum arg);
 extern void register_persistent_abort_backup_handler(void);
 extern SessionBackupState get_backup_status(void);
 
+extern void checkpoint_progress_start(int flags);
+extern void checkpoint_progress_update_param(int index, int64 val);
+extern void checkpoint_progress_end(void);
+
 /* File path names (all relative to $PGDATA) */
 #define RECOVERY_SIGNAL_FILE	"recovery.signal"
 #define STANDBY_SIGNAL_FILE		"standby.signal"
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7f1ee97f55..40f993c9b1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5353,6 +5353,15 @@
   proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
   proargnames => '{cmdtype,pid,datid,relid,param1,param2,param3,param4,param5,param6,param7,param8,param9,param10,param11,param12,param13,param14,param15,param16,param17,param18,param19,param20}',
   prosrc => 'pg_stat_get_progress_info' },
+{ oid => '560', descr => 'return checkpoint type',
+  proname => 'pg_stat_get_progress_checkpoint_type', prorettype => 'text',
+  proargtypes => 'int8', prosrc => 'pg_stat_get_progress_checkpoint_type' },
+{ oid => '561', descr => 'return checkpoint kind',
+  proname => 'pg_stat_get_progress_checkpoint_kind', prorettype => 'text',
+  proargtypes => 'int8', prosrc => 'pg_stat_get_progress_checkpoint_kind' },
+{ oid => '562', descr => 'return elapsed time of the checkpoint',
+  proname => 'pg_stat_get_progress_checkpoint_elapsed', prorettype => 'text',
+  proargtypes => 'int8', prosrc => 'pg_stat_get_progress_checkpoint_elapsed' },
 { oid => '3099',
   descr => 'statistics: information about currently active replication',
   proname => 'pg_stat_get_wal_senders', prorows => '10', proisstrict => 'f',
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index a28938caf4..19734405ed 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -151,4 +151,32 @@
 #define PROGRESS_COPY_TYPE_PIPE 3
 #define PROGRESS_COPY_TYPE_CALLBACK 4
 
+/* Progress parameters for checkpoint */
+#define PROGRESS_CHECKPOINT_TIMELINE                0
+#define PROGRESS_CHECKPOINT_KIND                    1
+#define PROGRESS_CHECKPOINT_LSN                     2
+#define PROGRESS_CHECKPOINT_START_TIMESTAMP         3
+#define PROGRESS_CHECKPOINT_PHASE                   4
+#define PROGRESS_CHECKPOINT_BUFFERS_TOTAL           5
+#define PROGRESS_CHECKPOINT_BUFFERS_PROCESSED       6
+#define PROGRESS_CHECKPOINT_BUFFERS_WRITTEN         7
+#define PROGRESS_CHECKPOINT_FILES_TOTAL             8
+#define PROGRESS_CHECKPOINT_FILES_SYNCED            9
+
+/* Phases of checkpoint (as advertised via PROGRESS_CHECKPOINT_PHASE) */
+#define PROGRESS_CHECKPOINT_PHASE_INIT                          0
+#define PROGRESS_CHECKPOINT_PHASE_REPLI_SLOTS                   1
+#define PROGRESS_CHECKPOINT_PHASE_SNAPSHOTS                     2
+#define PROGRESS_CHECKPOINT_PHASE_LOGICAL_REWRITE_MAPPINGS      3
+#define PROGRESS_CHECKPOINT_PHASE_CLOG_PAGES                    4
+#define PROGRESS_CHECKPOINT_PHASE_COMMITTS_PAGES                5
+#define PROGRESS_CHECKPOINT_PHASE_SUBTRANS_PAGES                6
+#define PROGRESS_CHECKPOINT_PHASE_MULTIXACT_PAGES               7
+#define PROGRESS_CHECKPOINT_PHASE_SLRU_PAGES                    8
+#define PROGRESS_CHECKPOINT_PHASE_BUFFERS                       9
+#define PROGRESS_CHECKPOINT_PHASE_FILE_SYNC                     10
+#define PROGRESS_CHECKPOINT_PHASE_TWO_PHASE                     11
+#define PROGRESS_CHECKPOINT_PHASE_OLD_XLOG_RECYCLE              12
+#define PROGRESS_CHECKPOINT_PHASE_FINALIZE                      13
+
 #endif
diff --git a/src/include/utils/backend_progress.h b/src/include/utils/backend_progress.h
index 47bf8029b0..02d51fb948 100644
--- a/src/include/utils/backend_progress.h
+++ b/src/include/utils/backend_progress.h
@@ -27,7 +27,8 @@ typedef enum ProgressCommandType
 	PROGRESS_COMMAND_CLUSTER,
 	PROGRESS_COMMAND_CREATE_INDEX,
 	PROGRESS_COMMAND_BASEBACKUP,
-	PROGRESS_COMMAND_COPY
+	PROGRESS_COMMAND_COPY,
+	PROGRESS_COMMAND_CHECKPOINT
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	20
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 1420288d67..fe6e31ca27 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1897,6 +1897,39 @@ pg_stat_progress_basebackup| SELECT s.pid,
     s.param4 AS tablespaces_total,
     s.param5 AS tablespaces_streamed
    FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20);
+pg_stat_progress_checkpoint| SELECT s.pid,
+    pg_stat_get_progress_checkpoint_type(s.param1) AS type,
+    pg_stat_get_progress_checkpoint_kind(s.param2) AS kind,
+    ( SELECT ('0/0'::pg_lsn + (
+                CASE
+                    WHEN (stat.lsn_int64 < 0) THEN pow((2)::numeric, (64)::numeric)
+                    ELSE (0)::numeric
+                END + (stat.lsn_int64)::numeric))
+           FROM ( SELECT s.param3) stat(lsn_int64)) AS start_lsn,
+    pg_stat_get_progress_checkpoint_elapsed(s.param4) AS elapsed_time,
+        CASE s.param5
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'checkpointing replication slots'::text
+            WHEN 2 THEN 'checkpointing snapshots'::text
+            WHEN 3 THEN 'checkpointing logical rewrite mappings'::text
+            WHEN 4 THEN 'checkpointing CLOG pages'::text
+            WHEN 5 THEN 'checkpointing CommitTs pages'::text
+            WHEN 6 THEN 'checkpointing SUBTRANS pages'::text
+            WHEN 7 THEN 'checkpointing MULTIXACT pages'::text
+            WHEN 8 THEN 'checkpointing SLRU pages'::text
+            WHEN 9 THEN 'checkpointing buffers'::text
+            WHEN 10 THEN 'performing sync requests'::text
+            WHEN 11 THEN 'performing two phase checkpoint'::text
+            WHEN 12 THEN 'recycling old XLOG files'::text
+            WHEN 13 THEN 'Finalizing'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param6 AS total_buffer_writes,
+    s.param7 AS buffers_processed,
+    s.param8 AS buffers_written,
+    s.param9 AS total_file_syncs,
+    s.param10 AS files_synced
+   FROM pg_stat_get_progress_info('CHECKPOINT'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20);
 pg_stat_progress_cluster| SELECT s.pid,
     s.datid,
     d.datname,
-- 
2.25.1

