v1-0003-Add-Standby-Recovery-Conflicts-BlockedOnly-Info.patch

text/plain

Filename: v1-0003-Add-Standby-Recovery-Conflicts-BlockedOnly-Info.patch
Type: text/plain
Part: 0
Message: Re: Add Information during standby recovery conflicts

Patch

Format: unified
Series: patch v1-0003
File+
doc/src/sgml/config.sgml 20 0
src/backend/storage/ipc/procsignal.c 36 0
src/backend/storage/ipc/standby.c 53 0
src/backend/utils/misc/guc.c 9 1
src/backend/utils/misc/postgresql.conf.sample 1 0
src/include/storage/procsignal.h 1 0
src/include/storage/standby.h 1 0
 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 '
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-log-recovery-conflicts-resolution" xreflabel="log_recovery_conflicts_resolution">
+      <term><varname>log_recovery_conflicts_resolution</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>log_recovery_conflicts_resolution</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Controls whether detailed information is produced when conflict
+        resolution occurs during standby recovery.
+       </para>
+
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</filename>
+        file or on the server command line.
+        The default is <literal>off</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-log-parameter-max-length" xreflabel="log_parameter_max_length">
       <term><varname>log_parameter_max_length</varname> (<type>integer</type>)
       <indexterm>
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);