sync-standalone-v2.patch

application/octet-stream

Filename: sync-standalone-v2.patch
Type: application/octet-stream
Part: 0
Message: Re: Standalone synchronous master

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v2
File+
doc/src/sgml/config.sgml 18 0
src/backend/postmaster/checkpointer.c 3 0
src/backend/replication/syncrep.c 90 1
src/backend/replication/walsender.c 7 0
src/backend/utils/misc/guc.c 10 0
src/backend/utils/misc/postgresql.conf.sample 5 0
src/include/replication/syncrep.h 5 0
src/include/replication/walsender_private.h 11 0
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0cc3296..e89d8aa 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..b2ee202 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -353,6 +353,7 @@ CheckpointerMain(void)
 
 	/* Do this once before starting the loop, then just at SIGHUP time. */
 	SyncRepUpdateSyncStandbysDefined();
+	SyncRepUpdateSyncStandaloneAllowed();
 
 	/*
 	 * Loop forever
@@ -382,6 +383,7 @@ CheckpointerMain(void)
 			ProcessConfigFile(PGC_SIGHUP);
 			/* update global shmem state for sync rep */
 			SyncRepUpdateSyncStandbysDefined();
+			SyncRepUpdateSyncStandaloneAllowed();
 		}
 		if (checkpoint_requested)
 		{
@@ -658,6 +660,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..393068c 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,17 @@ SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
 		return;
 	}
 
+
+	/*
+	 * Fast exit also if running in standalone mode
+	 * because there are no synchronous standbys connected
+	 */
+	if ( WalSndCtl->sync_standalone_master )
+	{
+		LWLockRelease(SyncRepLock);
+		return;
+	}
+
 	/*
 	 * Set our waitLSN so WALSender will know when to wake us, and add
 	 * ourselves to the queue.
@@ -326,6 +339,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.
+ * This function is called on connect/disconnect of standby WAL senders. Must hold SyncRepLock.
+ */
+bool SyncRepCheckIfStandaloneMaster()
+{
+	bool standby_connected = false;
+	int i = 0;
+
+	if (!SyncRepRequested() || !SyncStandbysDefined())
+		return false;
+
+	if ( ! WalSndCtl->sync_standalone_allowed )
+	{
+		WalSndCtl->sync_standalone_master = false;
+	        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 to standalone mode so wake up anyone that is waiting
+			 */
+			SyncRepWakeQueue(true);
+		}
+	}
+
+	return standby_connected;
+}
+
+/*
  * ===========================================================
  * Synchronous Replication functions for wal sender processes
  * ===========================================================
@@ -349,6 +419,7 @@ SyncRepInitConfig(void)
 	{
 		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
 		MyWalSnd->sync_standby_priority = priority;
+		SyncRepCheckIfStandaloneMaster();
 		LWLockRelease(SyncRepLock);
 		ereport(DEBUG1,
 			(errmsg("standby \"%s\" now has synchronous standby priority %u",
@@ -581,7 +652,6 @@ SyncRepUpdateSyncStandbysDefined(void)
 	if (sync_standbys_defined != WalSndCtl->sync_standbys_defined)
 	{
 		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
-
 		/*
 		 * If synchronous_standby_names has been reset to empty, it's futile
 		 * for backends to continue to waiting.  Since the user no longer
@@ -598,6 +668,25 @@ SyncRepUpdateSyncStandbysDefined(void)
 		 * the queue (and never wake up).  This prevents that.
 		 */
 		WalSndCtl->sync_standbys_defined = sync_standbys_defined;
+		LWLockRelease(SyncRepLock);
+	}
+}
+
+/*
+ * The background writer calls this as needed to update the shared
+ * sync_standalone_allowed flag. If the flag is enabled, then also check if
+ * any synchronous standby servers are connected in order to switch mode from sync
+ * replication to standalone mode.
+ */
+void
+SyncRepUpdateSyncStandaloneAllowed(void)
+{
+	if ( SyncRepStandaloneMasterAllowed != WalSndCtl->sync_standalone_allowed )
+	{
+		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+
+		WalSndCtl->sync_standalone_allowed = SyncRepStandaloneMasterAllowed;
+		SyncRepCheckIfStandaloneMaster();
 
 		LWLockRelease(SyncRepLock);
 	}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index ea86520..1da44e0 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -955,6 +955,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..954c79f 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -85,6 +85,17 @@ typedef struct
 	 */
 	bool		sync_standbys_defined;
 
+	/*
+	 * Is the synchronous master allowed to switch to standalone mode when no
+	 * synchronous standby servers are connected ? Protected by SyncRepLock.
+	 */
+	bool            sync_standalone_allowed;
+
+	/*
+	 * Is the synchronous master currently running in standalone mode ? Protected by SyncRepLock.
+	 */
+	bool            sync_standalone_master;
+
 	WalSnd		walsnds[1];		/* VARIABLE LENGTH ARRAY */
 } WalSndCtlData;