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;