sync-standalone-v1.patch.txt

text/plain

Filename: sync-standalone-v1.patch.txt
Type: text/plain
Part: 0
Message: Standalone synchronous master
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0cc3296..6367dcc 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -2182,6 +2182,24 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-synchronous-standalone-master" xreflabel="synchronous-standalone-master">
+      <term><varname>synchronous_standalone_master</varname> (<type>boolean</type>)</term>
+      <indexterm>
+       <primary><varname>synchronous_standalone_master</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+       Specifies how the master behaves when <xref linkend="guc-synchronous-commit">
+	is set to <literal>on</> and <xref linkend="guc-synchronous-standby-names"> is configured but no
+	 appropriate standby servers are currently connected. If enabled, the master will
+	 continue processing transactions alone. If disabled, all the transactions on the
+	 master are blocked until a synchronous standby has appeared.
+
+	The default is disabled.
+       </para>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index e9ae1e8..706af88 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -353,6 +353,8 @@ CheckpointerMain(void)
 
 	/* Do this once before starting the loop, then just at SIGHUP time. */
 	SyncRepUpdateSyncStandbysDefined();
+	SyncRepUpdateSyncStandaloneAllowed();
+	SyncRepCheckIfStandaloneMaster();
 
 	/*
 	 * Loop forever
@@ -382,6 +384,7 @@ CheckpointerMain(void)
 			ProcessConfigFile(PGC_SIGHUP);
 			/* update global shmem state for sync rep */
 			SyncRepUpdateSyncStandbysDefined();
+			SyncRepUpdateSyncStandaloneAllowed();
 		}
 		if (checkpoint_requested)
 		{
@@ -658,6 +661,7 @@ CheckpointWriteDelay(int flags, double progress)
 			ProcessConfigFile(PGC_SIGHUP);
 			/* update global shmem state for sync rep */
 			SyncRepUpdateSyncStandbysDefined();
+			SyncRepUpdateSyncStandaloneAllowed();
 		}
 
 		AbsorbFsyncRequests();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 95de6c7..fd3e782 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -59,6 +59,8 @@
 /* User-settable parameters for sync rep */
 char	   *SyncRepStandbyNames;
 
+bool       SyncRepStandaloneMasterAllowed;
+
 #define SyncStandbysDefined() \
 	(SyncRepStandbyNames != NULL && SyncRepStandbyNames[0] != '\0')
 
@@ -126,6 +128,20 @@ SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
 		return;
 	}
 
+
+	/*
+	 * Fast exit also if no synchronous standby servers are presently connected
+	 * and if the primary server has been configured to continue on without them.
+	 */
+	if ( SyncRepStandaloneMasterAllowed )
+	{
+		if ( ! SyncRepCheckIfStandaloneMaster() )
+		{
+			LWLockRelease(SyncRepLock);
+			return;
+		}
+	}
+
 	/*
 	 * Set our waitLSN so WALSender will know when to wake us, and add
 	 * ourselves to the queue.
@@ -326,6 +344,63 @@ SyncRepCleanupAtProcExit(void)
 }
 
 /*
+ * Check if the master should switch to standalone mode and stop trying
+ *  to wait for standby synchronization because there are no standby servers currently
+ * connected. If there are servers connected, then switch back and start waiting for them.
+ * Must hold SyncRepLock.
+ */
+bool SyncRepCheckIfStandaloneMaster()
+{
+	bool standby_connected = false;
+	int i = 0;
+
+	if (!SyncRepRequested() || !SyncStandbysDefined())
+		return false;
+
+	if ( ! WalSndCtl->sync_standalone_allowed )
+	        return false;
+
+	for (i = 0; i < max_wal_senders && ! standby_connected; i++)
+	{
+		volatile WalSnd *walsnd = &WalSndCtl->walsnds[i];
+		if ( walsnd->pid != 0 && walsnd->sync_standby_priority )
+		{
+			standby_connected = true;
+
+			if ( WalSndCtl->sync_standalone_master )
+			{
+				ereport(LOG,
+					(errmsg("waiting for standby synchronization"),
+					 errhidestmt(true)));
+
+				WalSndCtl->sync_standalone_master = false;
+			}
+		}
+	}
+
+	if ( ! standby_connected )
+	{
+		if ( ! WalSndCtl->sync_standalone_master )
+		{
+			ereport(LOG,
+				(errmsg("not waiting for standby synchronization"),
+				 errhidestmt(true)));
+
+			WalSndCtl->sync_standalone_master = true;
+
+			/*
+			 * We just switched mode and do not want to wait for standby sync anymore.
+			 * Wake others who may be waiting at this point
+			 */
+			SyncRepWakeQueue(true);
+		}
+	}
+
+	return standby_connected;
+
+}
+
+/*
  * ===========================================================
  * Synchronous Replication functions for wal sender processes
  * ===========================================================
@@ -603,6 +678,25 @@ SyncRepUpdateSyncStandbysDefined(void)
 	}
 }
 
+
+void
+SyncRepUpdateSyncStandaloneAllowed(void)
+{
+	bool value = SyncRepStandaloneMasterAllowed;
+
+	if ( SyncRepStandaloneMasterAllowed != WalSndCtl->sync_standalone_allowed )
+	{
+		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+
+		if ( SyncRepStandaloneMasterAllowed )
+			SyncRepWakeQueue(true);
+
+		WalSndCtl->sync_standalone_allowed = SyncRepStandaloneMasterAllowed;
+
+		LWLockRelease(SyncRepLock);
+	}
+}
+
 #ifdef USE_ASSERT_CHECKING
 static bool
 SyncRepQueueIsOrderedByLSN(void)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index ea86520..ddfaa09 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -932,11 +932,23 @@ InitWalSnd(void)
 		}
 	}
 	if (MyWalSnd == NULL)
+	{
 		ereport(FATAL,
 				(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
 				 errmsg("number of requested standby connections "
 						"exceeds max_wal_senders (currently %d)",
 						max_wal_senders)));
+	}
+	else
+	{
+		/*
+		 * A standby just connected, check if the master should
+		 * switch from standalone to synchronous mode.
+		 */
+		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+		SyncRepCheckIfStandaloneMaster();
+		LWLockRelease(SyncRepLock);
+	}
 
 	/* Arrange to clean up at walsender exit */
 	on_shmem_exit(WalSndKill, 0);
@@ -955,6 +967,13 @@ WalSndKill(int code, Datum arg)
 	MyWalSnd->pid = 0;
 	DisownLatch(&MyWalSnd->latch);
 
+	/*
+	 * Check if this was the last standby
+	 */
+	LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+	SyncRepCheckIfStandaloneMaster();
+	LWLockRelease(SyncRepLock);
+
 	/* WalSnd struct isn't mine anymore */
 	MyWalSnd = NULL;
 }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index da7b6d4..f26ee7a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1375,6 +1375,16 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"synchronous_standalone_master", PGC_SIGHUP, REPLICATION_MASTER,
+			gettext_noop("Specifies whether we allow the master to process transactions alone when there is no connected standby."),
+			NULL
+		},
+		&SyncRepStandaloneMasterAllowed,
+		false,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 315db46..812bdf0 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -215,6 +215,11 @@
 				# from standby(s); '*' = all
 #vacuum_defer_cleanup_age = 0	# number of xacts by which cleanup is delayed
 
+#synchronous_standalone_master = off	# Whether the master can continue processing
+					# commits when no sync standbys are connected
+					# or if it has to wait until one connects.
+
+
 # - Standby Servers -
 
 # These settings are ignored on a master server
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index 65b725f..0699e73 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -23,6 +23,8 @@
 /* user-settable parameters for synchronous replication */
 extern char *SyncRepStandbyNames;
 
+extern bool SyncRepStandaloneMasterAllowed;
+
 /* called by user backend */
 extern void SyncRepWaitForLSN(XLogRecPtr XactCommitLSN);
 
@@ -35,6 +37,9 @@ extern void SyncRepReleaseWaiters(void);
 
 /* called by wal writer */
 extern void SyncRepUpdateSyncStandbysDefined(void);
+extern void SyncRepUpdateSyncStandaloneAllowed(void);
+
+extern bool SyncRepCheckIfStandaloneMaster(void);
 
 /* called by various procs */
 extern int	SyncRepWakeQueue(bool all);
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index be7a341..d1dc606 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -85,6 +85,18 @@ typedef struct
 	 */
 	bool		sync_standbys_defined;
 
+	/*
+	 * Whether the synchronous master is allowed to switch to standalone mode
+	 * when there are not standby servers connected.
+	 */
+	bool            sync_standalone_allowed;
+
+	/*
+	 * Whether the synchronous master is currently running in standalone mode
+	 * because there are no WAL senders connected.
+	 */
+	bool            sync_standalone_master;
+
 	WalSnd		walsnds[1];		/* VARIABLE LENGTH ARRAY */
 } WalSndCtlData;