From a0e8d75223fa95dbec1e422eacaef336e45c2008 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Thu, 13 Nov 2025 15:00:43 +0100 Subject: [PATCH 1/2] Add a hook for Checkpoint processing. There are many situations in which a Postgres plugin may need to maintain its internal state across restarts or crashes. Sometimes it wants to synchronise its state on logical replicas or be saved in a backup employing custom RMGR and WAL records. For statistical extensions, such as pg_stat_statements, it is okay to save their state on postmaster shutdown. However, business extensions may want to maintain more actual state, periodically dumping it to a disk file or using WAL and xact callbacks to be as close as possible to the current database state. Checkpoint is a key moment where the DBMS performs disk synchronisation and cuts the WAL. It is a good time to do the same thing for a plugin, too. Moreover, the plugin is sure that nothing important will be lost with the WAL cut. Discussion: https://www.postgresql.org/message-id/CANbhV-E4pTWeF-DsdaGsOrjJNFWPaR%2BDstjrnkqvf9JFFgOKKQ%40mail.gmail.com --- src/backend/access/transam/xlog.c | 15 +++++++++++++++ src/include/access/xlog.h | 4 ++++ src/tools/pgindent/typedefs.list | 1 + 3 files changed, 20 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 22d0a2e8c3a..c7c0b226724 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -157,6 +157,13 @@ int wal_segment_size = DEFAULT_XLOG_SEG_SIZE; */ int CheckPointSegments; +/* + * Hook for plugins to take control during checkpoint processing. All + * preparation procedures have already been done, and only the sync needs + * to be done. + */ +Checkpoint_hook_type Checkpoint_hook = NULL; + /* Estimated distance between checkpoints, in bytes */ static double CheckPointDistanceEstimate = 0; static double PrevCheckPointDistance = 0; @@ -7594,6 +7601,14 @@ CheckPointGuts(XLogRecPtr checkPointRedo, int flags) CheckPointPredicate(); CheckPointBuffers(flags); + /* + * Allow a plugin that depends on a custom RMGR to retain its state through + * reboots or crashes by following specific steps, ensuring that essential + * WAL records are not truncated. + */ + if (Checkpoint_hook) + Checkpoint_hook(checkPointRedo, flags); + /* Perform all queued up fsyncs */ TRACE_POSTGRESQL_BUFFER_CHECKPOINT_SYNC_START(); CheckpointStats.ckpt_sync_t = GetCurrentTimestamp(); diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 605280ed8fb..5c071974557 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -198,6 +198,10 @@ typedef enum WALAvailability struct XLogRecData; struct XLogReaderState; +/* Hook for plugins to get control at the end of a CheckPoint */ +typedef void (*Checkpoint_hook_type)(XLogRecPtr checkPointRedo, int flags); +extern PGDLLIMPORT Checkpoint_hook_type Checkpoint_hook; + extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata, XLogRecPtr fpw_lsn, uint8 flags, diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 23bce72ae64..6ca05499081 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -413,6 +413,7 @@ CatalogIndexState ChangeVarNodes_callback ChangeVarNodes_context CheckPoint +Checkpoint_hook_type CheckPointStmt CheckpointStatsData CheckpointerRequest -- 2.51.2