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; + + synchronous_standalone_master (boolean) + + synchronous_standalone_master configuration parameter + + + + Specifies how the master behaves when + is set to on and 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. + + + + 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;