From 285d43afae1daf9cad9d8473e9576f3d33e3ab6d Mon Sep 17 00:00:00 2001 From: GremSnoort Date: Mon, 27 Apr 2026 22:45:37 +0300 Subject: [PATCH 1/2] Add recovery target reached hook for extensions Add a new RecoveryTargetReachedHook that is invoked after PostgreSQL reaches a recovery target and before recovery_target_action is applied. The hook receives a RecoveryTargetReachedInfo structure with the ReadRecPtr/EndRecPtr of the WAL record at the recovery stop boundary. This gives extensions an explicit recovery stop-boundary context for synchronizing or finalizing custom replay state before PostgreSQL exposes the target-reached state. --- src/backend/access/transam/xlogrecovery.c | 18 +++++++++++++++ src/include/access/xlogrecovery.h | 28 ++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index fecdc0da4f8..9b73fd09423 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -1798,6 +1798,22 @@ PerformWalRecovery(void) * Resource Managers may choose to do permanent corrective actions * at end of recovery. */ + + /* + * Pass the exact stop-boundary metadata to extensions so they can + * synchronize custom replay state against the record that caused + * recovery to stop. + */ + if (RecoveryTargetReachedHook != NULL) + { + RecoveryTargetReachedInfo recoveryTargetReachedInfo; + + recoveryTargetReachedInfo.recordPtr = xlogreader->ReadRecPtr; + recoveryTargetReachedInfo.recordEndPtr = xlogreader->EndRecPtr; + + RecoveryTargetReachedHook(&recoveryTargetReachedInfo); + } + switch (recoveryTargetAction) { case RECOVERY_TARGET_ACTION_SHUTDOWN: @@ -4539,6 +4555,8 @@ GetReplayXlogPtrHookType GetReplayXlogPtrHook = NULL; RecoveryStopsBeforeHookType RecoveryStopsBeforeHook = NULL; +RecoveryTargetReachedHookType RecoveryTargetReachedHook = NULL; + /* * Get effective latest redo apply position. * diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h index 8ab6e99f86d..9ce322eb66f 100644 --- a/src/include/access/xlogrecovery.h +++ b/src/include/access/xlogrecovery.h @@ -54,6 +54,25 @@ typedef bool (*RecoveryStopsBeforeHookType) (XLogReaderState *record, TransactionId *recordXid, TimestampTz *recordXtime); +/* + * Metadata describing the recovery stop boundary that PostgreSQL has just + * reached. This is passed to extensions so they can synchronize or finalize + * their own replay state before recovery_target_action is applied. + */ +typedef struct RecoveryTargetReachedInfo +{ + XLogRecPtr recordPtr; /* ReadRecPtr of the record at the stop boundary */ + XLogRecPtr recordEndPtr; /* EndRecPtr of the record at the stop boundary */ +} RecoveryTargetReachedInfo; + +/* + * Hook for extensions to synchronize or finalize custom replay state after + * PostgreSQL has reached a recovery target, but before recovery_target_action + * is applied. The hook receives explicit metadata describing the stop + * boundary that was reached. + */ +typedef void (*RecoveryTargetReachedHookType) (const RecoveryTargetReachedInfo *info); + /* User-settable GUC parameters */ extern PGDLLIMPORT bool recoveryTargetInclusive; extern PGDLLIMPORT int recoveryTargetAction; @@ -86,11 +105,18 @@ extern PGDLLIMPORT bool StandbyMode; extern PGDLLIMPORT GetReplayXlogPtrHookType GetReplayXlogPtrHook; /* - * Hook for extensions to be able to decides to stop applying the WAL files + * Hook for extensions to be able to decide whether to stop applying WAL * based on custom WAL records. */ extern PGDLLIMPORT RecoveryStopsBeforeHookType RecoveryStopsBeforeHook; +/* + * Hook for extensions to synchronize or finalize custom replay state after + * PostgreSQL has reached a recovery target, but before recovery_target_action + * is applied. + */ +extern PGDLLIMPORT RecoveryTargetReachedHookType RecoveryTargetReachedHook; + extern Size XLogRecoveryShmemSize(void); extern void XLogRecoveryShmemInit(void); From d475d30f2accd0cc6878614e3835f2bd5652ea60 Mon Sep 17 00:00:00 2001 From: GremSnoort Date: Tue, 28 Apr 2026 12:56:22 +0300 Subject: [PATCH 2/2] Restore stop-after flag in recovery target hook metadata --- src/backend/access/transam/xlogrecovery.c | 1 + src/include/access/xlogrecovery.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 9b73fd09423..8f5d3329fdb 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -1808,6 +1808,7 @@ PerformWalRecovery(void) { RecoveryTargetReachedInfo recoveryTargetReachedInfo; + recoveryTargetReachedInfo.recoveryStopAfter = recoveryStopAfter; recoveryTargetReachedInfo.recordPtr = xlogreader->ReadRecPtr; recoveryTargetReachedInfo.recordEndPtr = xlogreader->EndRecPtr; diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h index 9ce322eb66f..d2fbe408dbc 100644 --- a/src/include/access/xlogrecovery.h +++ b/src/include/access/xlogrecovery.h @@ -61,6 +61,7 @@ typedef bool (*RecoveryStopsBeforeHookType) (XLogReaderState *record, */ typedef struct RecoveryTargetReachedInfo { + bool recoveryStopAfter; XLogRecPtr recordPtr; /* ReadRecPtr of the record at the stop boundary */ XLogRecPtr recordEndPtr; /* EndRecPtr of the record at the stop boundary */ } RecoveryTargetReachedInfo;