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; + + 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..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;