doc/src/sgml/config.sgml | 20 ++++++++++
src/backend/storage/ipc/procsignal.c | 36 ++++++++++++++++++
src/backend/storage/ipc/standby.c | 53 +++++++++++++++++++++++++++
src/backend/utils/misc/guc.c | 10 ++++-
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/storage/procsignal.h | 1 +
src/include/storage/standby.h | 1 +
7 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index aca8f73a50..d0c61f9875 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6763,6 +6763,26 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
+
+ log_recovery_conflicts_resolution (boolean)
+
+ log_recovery_conflicts_resolution configuration parameter
+
+
+
+
+ Controls whether detailed information is produced when conflict
+ resolution occurs during standby recovery.
+
+
+
+ This parameter can only be set in the postgresql.conf
+ file or on the server command line.
+ The default is off.
+
+
+
+
log_parameter_max_length (integer)
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c809196d06..f83176cfa7 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -576,3 +576,39 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
errno = save_errno;
}
+
+extern const char *get_procsignal_reason_desc(ProcSignalReason reason)
+ {
+ const char *reasonDesc = "unknown reason";
+
+ switch (reason)
+ {
+ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ reasonDesc = "buffer pin";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ reasonDesc = "lock";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ reasonDesc = "tablespace";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ reasonDesc = "snapshot";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ reasonDesc = "deadlock";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ reasonDesc = "database";
+ break;
+ case PROCSIG_CATCHUP_INTERRUPT:
+ case PROCSIG_NOTIFY_INTERRUPT:
+ case PROCSIG_PARALLEL_MESSAGE:
+ case PROCSIG_WALSND_INIT_STOPPING:
+ case NUM_PROCSIGNALS:
+ break;
+ default:
+ break;
+ }
+ return reasonDesc;
+}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 9e0d5ec257..b2aa5ce7cd 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -39,6 +39,7 @@
int vacuum_defer_cleanup_age;
int max_standby_archive_delay = 30 * 1000;
int max_standby_streaming_delay = 30 * 1000;
+bool log_recovery_conflicts_resolution = false;
static HTAB *RecoveryLockLists;
@@ -215,6 +216,42 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info)
return false;
}
+/*
+ * Log the recovery conflict
+ */
+static void
+LogRecoveryConflict(VirtualTransactionId *waitlist, ProcSignalReason reason)
+{
+
+ VirtualTransactionId *vxids;
+ StringInfoData buf;
+ int count;
+
+ if (waitlist) {
+ vxids = waitlist;
+ count = 0;
+ initStringInfo(&buf);
+
+ while (VirtualTransactionIdIsValid(*vxids))
+ {
+ count++;
+ if (count > 1)
+ appendStringInfo(&buf,", ");
+
+ appendStringInfo(&buf,"%d/%u",vxids->backendId,vxids->localTransactionId);
+ vxids++;
+ }
+ ereport(LOG,
+ (errmsg("recovery is resolving recovery conflict on %s", get_procsignal_reason_desc(reason)),
+ (errdetail_log_plural("Conflicting virtual transaction id: %s.",
+ "Conflicting virtual transaction ids: %s.", count, buf.data))));
+ pfree(buf.data);
+ }
+ else
+ ereport(LOG,
+ (errmsg("recovery is resolving recovery conflict on %s", get_procsignal_reason_desc(reason))));
+}
+
/*
* This is the main executioner for any query backend that conflicts with
* recovery processing. Judgement has already been passed on it within
@@ -232,6 +269,7 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
{
TimestampTz waitStart = 0;
char *new_status;
+ bool logged = false;
/* Fast exit, to avoid a kernel call if there's no work to be done. */
if (!VirtualTransactionIdIsValid(*waitlist))
@@ -249,6 +287,12 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
/* wait until the virtual xid is gone */
while (!VirtualXactLock(*waitlist, false))
{
+ /* Log the recovery conflict */
+ if (log_recovery_conflicts_resolution && !logged
+ && TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(), max_standby_streaming_delay)) {
+ LogRecoveryConflict(waitlist, reason);
+ logged = true;
+ }
/*
* Report via ps if we have been waiting for more than 500 msec
* (should that be configurable?)
@@ -370,6 +414,11 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
* block during InitPostgres() and then disconnect when they see the
* database has been removed.
*/
+
+ /* Log the recovery conflict */
+ if (log_recovery_conflicts_resolution)
+ LogRecoveryConflict(NULL, PROCSIG_RECOVERY_CONFLICT_DATABASE);
+
while (CountDBBackends(dbid) > 0)
{
CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true);
@@ -609,6 +658,10 @@ StandbyTimeoutHandler(void)
/* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
+ /* Log the recovery conflict */
+ if (log_recovery_conflicts_resolution)
+ LogRecoveryConflict(NULL, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2f3e0a70e0..96316b0415 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1594,7 +1594,15 @@ static struct config_bool ConfigureNamesBool[] =
false,
NULL, NULL, NULL
},
-
+ {
+ {"log_recovery_conflicts_resolution", PGC_SIGHUP, LOGGING_WHAT,
+ gettext_noop("Logs standby recovery conflicts resolution."),
+ NULL
+ },
+ &log_recovery_conflicts_resolution,
+ false,
+ NULL, NULL, NULL
+ },
{
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
gettext_noop("Logs the host name in the connection logs."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ac02bd0c00..77328836f8 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -548,6 +548,7 @@
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_recovery_conflicts_resolution = off # log standby recovery conflicts
#log_parameter_max_length = -1 # when logging statements, limit logged
# bind-parameter values to N bytes;
# -1 means print in full, 0 disables
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a0c0bc3ce5..c57413b06d 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -70,5 +70,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
extern void ProcessProcSignalBarrier(void);
extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern const char *get_procsignal_reason_desc(ProcSignalReason reason);
#endif /* PROCSIGNAL_H */
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index cfbe426e5a..bcc50d4dd1 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -23,6 +23,7 @@
extern int vacuum_defer_cleanup_age;
extern int max_standby_archive_delay;
extern int max_standby_streaming_delay;
+extern bool log_recovery_conflicts_resolution;
extern void InitRecoveryTransactionEnvironment(void);
extern void ShutdownRecoveryTransactionEnvironment(void);