From cfc70e656d7226d7f6050a99b33eeb9d09841584 Mon Sep 17 00:00:00 2001
From: Greg Nancarrow <gregn4422@gmail.com>
Date: Tue, 1 Dec 2020 14:17:38 +1100
Subject: [PATCH v20 1/2] Add "in_hot_standby" reportable GUC and make
 "transaction_read_only" GUC reportable.

Add "in_hot_standby" as a GUC_REPORT variable, to indicate to clients when
hot-standby is currently active, by having the end-of-query function check (when
needed) to see if the active hot-standby value changed. (Implementation: Tom Lane)
Also, enhance "transaction_read_only" to be a GUC_REPORT variable, for client
connections to read-only/read-write servers.
Making these GUC variables reportable avoids having to execute a query
post-connection in order to determine whether a host is in hot-standby mode or
is read-write (and reduces time to make the connection).

Discussion: https://www.postgresql.org/message-id/flat/CAF3+xM+8-ztOkaV9gHiJ3wfgENTq97QcjXQt+rbFQ6F7oNzt9A@mail.gmail.com
---
 doc/src/sgml/high-availability.sgml | 11 +++++----
 src/backend/utils/misc/check_guc    |  2 +-
 src/backend/utils/misc/guc.c        | 48 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index 19d7bd2..1f5aef9 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -1848,8 +1848,9 @@ if (!triggered)
    </para>
 
    <para>
-    During hot standby, the parameter <varname>transaction_read_only</varname> is always
-    true and may not be changed.  But as long as no attempt is made to modify
+    During hot standby, the parameters <varname>in_hot_standby</varname> and
+    <varname>transaction_read_only</varname> are always true and may not be
+    changed. But as long as no attempt is made to modify
     the database, connections during hot standby will act much like any other
     database connection.  If failover or switchover occurs, the database will
     switch to normal processing mode.  Sessions will remain connected while the
@@ -1859,9 +1860,9 @@ if (!triggered)
    </para>
 
    <para>
-    Users will be able to tell whether their session is read-only by
-    issuing <command>SHOW transaction_read_only</command>.  In addition, a set of
-    functions (<xref linkend="functions-recovery-info-table"/>) allow users to
+    Users will be able to tell whether hot standby is currently active for their
+    session by issuing <command>SHOW in_hot_standby</command>.  In addition, a set
+    of functions (<xref linkend="functions-recovery-info-table"/>) allow users to
     access information about the standby server. These allow you to write
     programs that are aware of the current state of the database. These
     can be used to monitor the progress of recovery, or to allow you to
diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc
index 416a087..5e7a54a 100755
--- a/src/backend/utils/misc/check_guc
+++ b/src/backend/utils/misc/check_guc
@@ -21,7 +21,7 @@ is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \
 pre_auth_delay role seed server_encoding server_version server_version_num \
 session_authorization trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks \
 trace_notify trace_userlocks transaction_isolation transaction_read_only \
-zero_damaged_pages"
+zero_damaged_pages in_hot_standby"
 
 ### What options are listed in postgresql.conf.sample, but don't appear
 ### in guc.c?
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 245a347..311474e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -209,6 +209,7 @@ static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
 static const char *show_data_directory_mode(void);
+static const char *show_in_hot_standby(void);
 static bool check_backtrace_functions(char **newval, void **extra, GucSource source);
 static void assign_backtrace_functions(const char *newval, void *extra);
 static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
@@ -607,6 +608,8 @@ static int	max_identifier_length;
 static int	block_size;
 static int	segment_size;
 static int	wal_block_size;
+static bool in_hot_standby;
+static bool last_reported_in_hot_standby;
 static bool data_checksums;
 static bool integer_datetimes;
 static bool assert_enabled;
@@ -1618,7 +1621,7 @@ static struct config_bool ConfigureNamesBool[] =
 		{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the current transaction's read-only status."),
 			NULL,
-			GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+			GUC_REPORT | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&XactReadOnly,
 		false,
@@ -1845,6 +1848,17 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"in_hot_standby", PGC_INTERNAL, PRESET_OPTIONS,
+			gettext_noop("Shows whether hot standby is currently active."),
+			NULL,
+			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+		},
+		&in_hot_standby,
+		false,
+		NULL, NULL, show_in_hot_standby
+	},
+
+	{
 		{"allow_system_table_mods", PGC_SUSET, DEVELOPER_OPTIONS,
 			gettext_noop("Allows modifications of the structure of system tables."),
 			NULL,
@@ -6267,6 +6281,9 @@ BeginReportingGUCOptions(void)
 			ReportGUCOption(conf);
 	}
 
+	/* Hack for in_hot_standby: remember the value we just sent */
+	last_reported_in_hot_standby = in_hot_standby;
+
 	report_needed = false;
 }
 
@@ -6290,6 +6307,23 @@ ReportChangedGUCOptions(void)
 	if (!reporting_enabled)
 		return;
 
+	/*
+	 * Since in_hot_standby isn't actually changed by normal GUC actions, we
+	 * need a hack to check whether a new value needs to be reported to the
+	 * client.  For speed, we rely on the assumption that it can never
+	 * transition from false to true.
+	 */
+	if (last_reported_in_hot_standby && !RecoveryInProgress())
+	{
+		struct config_generic *record;
+
+		record = find_option("in_hot_standby", false, ERROR);
+		Assert(record != NULL);
+		record->status |= GUC_NEEDS_REPORT;
+		report_needed = true;
+		last_reported_in_hot_standby = false;
+	}
+
 	/* Quick exit if no values have been changed */
 	if (!report_needed)
 		return;
@@ -11783,6 +11817,18 @@ show_data_directory_mode(void)
 	return buf;
 }
 
+static const char *
+show_in_hot_standby(void)
+{
+	/*
+	 * Unlike most show hooks, this has a side-effect of updating the dummy
+	 * GUC variable to contain the value last shown.  See confederate code in
+	 * BeginReportingGUCOptions and ReportChangedGUCOptions.
+	 */
+	in_hot_standby = RecoveryInProgress();
+	return in_hot_standby ? "on" : "off";
+}
+
 /*
  * We split the input string, where commas separate function names
  * and certain whitespace chars are ignored, into a \0-separated (and
-- 
1.8.3.1

