*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 1775,1780 **** include 'filename'
--- 1775,1836 ----
+
+ enable_standalone_master (boolean)
+
+ enable_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.
+
+
+
+
+
+ master_to_standalone_cmd (enum)
+
+ master_to_standalone_cmd> configuration parameter
+
+
+
+ This command is executed before master switches from sync mode to standalone
+ mode, this command is executed and if this command execution returns zero(i.e. success),
+ then only master can switch to standalone mode. Incase of failure of the given
+ command execution, the master mode remains same i.e. it remains in sync mode.
+
+
+ The user should provide some command, which does some operation, which DBA can
+ check later to determine the master mode. E.g. a command can be given which can
+ create some files to indicate that master has got switched to standalone mode.
+
+
+
+
+
+ master_to_sync_cmd (enum)
+
+ master_to_sync_cmd> configuration parameter
+
+
+
+ This command is executed before master switches from standalone mode to sync mode
+ mode, this command is executed and if this command execution returns zero(i.e. success),
+ then only master can switch to sync mode. Incase of failure of the given
+ command execution, the master mode remains same i.e. it remains in standalone mode.
+
+
+ The user should provide command reverse to master_to_standalone_cmd>, so that
+ it can undo the changes made. This will help DBA to determine that the master is running in
+ sync mode.
+
+
+
+
wal_sync_method (enum)
*** a/src/backend/postmaster/checkpointer.c
--- b/src/backend/postmaster/checkpointer.c
***************
*** 1354,1359 **** UpdateSharedMemoryConfig(void)
--- 1354,1360 ----
{
/* update global shmem state for sync rep */
SyncRepUpdateSyncStandbysDefined();
+ SyncRepUpdateSyncStandaloneMasterAllowed();
/*
* If full_page_writes has been changed by SIGHUP, we update it in shared
*** a/src/backend/replication/syncrep.c
--- b/src/backend/replication/syncrep.c
***************
*** 60,65 ****
--- 60,76 ----
/* User-settable parameters for sync rep */
char *SyncRepStandbyNames;
+ /* Commands to be executed to indicate change in sync mode*/
+ char *master_to_standalone_cmd; /* Master switch to standalone mode*/
+ char *master_to_sync_cmd; /* Master switch to sync mode*/
+
+ /*
+ * To control whether a master configured with synchronous_commit = on is
+ * allowed to stop waiting for standby WAL sync when all synchronous
+ * standby WAL senders are disconnected.
+ */
+ bool enable_standalone_master = false;
+
#define SyncStandbysDefined() \
(SyncRepStandbyNames != NULL && SyncRepStandbyNames[0] != '\0')
***************
*** 127,132 **** SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
--- 138,153 ----
return;
}
+ /*
+ * Return without waiting for LSN if there is no synchronous standbys
+ * connected
+ */
+ if (WalSndCtl->sync_master_in_standalone_mode)
+ {
+ LWLockRelease(SyncRepLock);
+ return;
+ }
+
/*
* Set our waitLSN so WALSender will know when to wake us, and add
* ourselves to the queue.
***************
*** 350,355 **** SyncRepInitConfig(void)
--- 371,384 ----
{
LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
MyWalSnd->sync_standby_priority = priority;
+
+
+ /*
+ * Synchronous standby is starting, so we should change the standalone
+ * sync_master_in_standalone_mode, if required.
+ */
+ SyncRepCheckSyncStdbyAlive();
+
LWLockRelease(SyncRepLock);
ereport(DEBUG1,
(errmsg("standby \"%s\" now has synchronous standby priority %u",
***************
*** 710,712 **** assign_synchronous_commit(int newval, void *extra)
--- 739,874 ----
break;
}
}
+
+ /*
+ * The checkpointer calls this as needed to update the shared
+ * sync_standbalone_master_allowed flag, so that backends don't remain
+ * permanently wedged if sync_standbalone_master_allowed is unset.
+ * It's safe to check the current value
+ * without the lock, because it's only ever updated by one process. But we
+ * must take the lock to change it.
+ */
+ void
+ SyncRepUpdateSyncStandaloneMasterAllowed()
+ {
+ /*
+ * Check if new value of parameter is same as earlier,
+ * if not then change in shared memory. Since here were enabling this
+ * parameter now only, so it may happen that there were no synchronous
+ * standby but master has not gone in stand-alone mode because it was
+ * not configured to do so.
+ */
+ if (enable_standalone_master != WalSndCtl->sync_standbalone_master_allowed)
+ {
+ LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+
+ WalSndCtl->sync_standbalone_master_allowed = enable_standalone_master;
+ (void)SyncRepCheckSyncStdbyAlive();
+
+ LWLockRelease(SyncRepLock);
+ }
+ }
+
+ /*
+ * Loop through all sender task and check if there is any
+ * synchronous standby is alive. If alive then master needs
+ * to continue to wait for synchronous standby otherwise,
+ * it does not have to and it can switch to standalone mode.
+ * Whenever mode is changing from one to another then
+ * log the appropriate log message, which will be used by
+ * DBA.
+ */
+ void
+ SyncRepCheckSyncStdbyAlive()
+ {
+ bool sync_standby_connected = false;
+ int loop = 0;
+
+ /*
+ * If it is in recovery mode (i.e. standby) then no need to check for
+ * synchronous standbys
+ */
+ if (RecoveryInProgress())
+ {
+ WalSndCtl->sync_master_in_standalone_mode = false;
+ return;
+ }
+
+ if (!SyncRepRequested() || !SyncStandbysDefined())
+ {
+ WalSndCtl->sync_master_in_standalone_mode = false;
+ return;
+ }
+
+ if ( !(WalSndCtl->sync_standbalone_master_allowed))
+ {
+ /*
+ * May be synchronous_standalone_allowed is changed to disable, then
+ * master should run in sync mode.
+ */
+ if (WalSndCtl->sync_master_in_standalone_mode && !system(master_to_sync_cmd))
+ {
+ ereport(LOG,
+ (errmsg("Master switched to sync mode: "
+ "Waiting for standby synchronisation")));
+ WalSndCtl->sync_master_in_standalone_mode = false;
+ }
+
+ return;
+ }
+
+ for (loop = 0; loop < max_wal_senders; loop++)
+ {
+ volatile WalSnd *walsnd = &WalSndCtl->walsnds[loop];
+
+ /*
+ * Check if this synchronous standby and its pid is not zero i.e. synchronous
+ * standby is alive.
+ */
+ if (walsnd->sync_standby_priority && walsnd->pid != 0)
+ {
+ sync_standby_connected = true;
+ if (WalSndCtl->sync_master_in_standalone_mode && !system(master_to_sync_cmd))
+ {
+ /*
+ * This log should be used by DBA to check if master
+ * was running in synchronoisation mode
+ */
+ ereport(LOG,
+ (errmsg("Master switched to sync mode: "
+ "Waiting for standby synchronisation")));
+ WalSndCtl->sync_master_in_standalone_mode = false;
+ }
+
+ return;
+ }
+ }
+
+ if (!sync_standby_connected)
+ {
+ if (!(WalSndCtl->sync_master_in_standalone_mode) && !system(master_to_standalone_cmd))
+ {
+ /*
+ * This log should be used by DBA to check if master
+ * was running in standalone mode
+ */
+ ereport(LOG,
+ (errmsg("Master switched to standalone mode: "
+ "Not waiting for standby synchronisation")));
+
+ WalSndCtl->sync_master_in_standalone_mode = true;
+
+ /*
+ * If there is any waiting sender, then wake-up them as
+ * master has switched to standalone mode
+ */
+ for (loop = 0; loop < NUM_SYNC_REP_WAIT_MODE; loop++)
+ {
+ SyncRepWakeQueue(true, loop);
+ }
+ }
+ }
+
+ return;
+ }
+
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 1187,1196 **** InitWalSenderSlot(void)
{
/*
* Found a free slot. Reserve it for us.
! */
walsnd->pid = MyProcPid;
walsnd->sentPtr = InvalidXLogRecPtr;
walsnd->state = WALSNDSTATE_STARTUP;
SpinLockRelease(&walsnd->mutex);
/* don't need the lock anymore */
OwnLatch((Latch *) &walsnd->latch);
--- 1187,1198 ----
{
/*
* Found a free slot. Reserve it for us.
! */
!
walsnd->pid = MyProcPid;
walsnd->sentPtr = InvalidXLogRecPtr;
walsnd->state = WALSNDSTATE_STARTUP;
+ walsnd->sync_standby_priority = 0;
SpinLockRelease(&walsnd->mutex);
/* don't need the lock anymore */
OwnLatch((Latch *) &walsnd->latch);
***************
*** 1223,1228 **** WalSndKill(int code, Datum arg)
--- 1225,1241 ----
MyWalSnd->pid = 0;
DisownLatch(&MyWalSnd->latch);
+ /*
+ * Here one standby is going down, then check if it was synchronous
+ * standby and also there is no more synchronous standby, if yes
+ * then wake all waiting transaction and also change the master
+ * mode to standalone
+ */
+ LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
+ SyncRepCheckSyncStdbyAlive();
+ LWLockRelease(SyncRepLock);
+
+
/* WalSnd struct isn't mine anymore */
MyWalSnd = NULL;
}
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1470,1475 **** static struct config_bool ConfigureNamesBool[] =
--- 1470,1486 ----
NULL, NULL, NULL
},
+ {
+ {"enable_standalone_master", PGC_SIGHUP, REPLICATION_MASTER,
+ gettext_noop("Enable master to continue as standbalone on sync standbys failure."),
+ NULL,
+ },
+ &enable_standalone_master,
+ false,
+ NULL, NULL, NULL
+ },
+
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
***************
*** 3151,3156 **** static struct config_string ConfigureNamesString[] =
--- 3162,3190 ----
check_application_name, assign_application_name, NULL
},
+ {
+ {"master_to_standalone_cmd", PGC_SIGHUP, REPLICATION_MASTER,
+ gettext_noop("Cmd to be executed to indicate DBA, master switched to standalone mode"),
+ NULL,
+ GUC_LIST_INPUT
+ },
+ &master_to_standalone_cmd,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"master_to_sync_cmd", PGC_SIGHUP, REPLICATION_MASTER,
+ gettext_noop("Cmd to be executed to indicate DBA, master switched to sync mode"),
+ NULL,
+ GUC_LIST_INPUT
+ },
+ &master_to_sync_cmd,
+ "",
+ NULL, NULL, NULL
+ },
+
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 227,232 ****
--- 227,242 ----
#synchronous_standby_names = '' # standby servers that provide sync rep
# comma-separated list of application_name
# from standby(s); '*' = all
+
+ #enable_standalone_master = off #Whether master is allowed to continue
+ #as standbalone after sync standby failure
+
+ #master_to_standalone_cmd = '' #Execute this command to indicate DBA that
+ # master node has switched to standalone mode.
+
+ #master_to_sync_cmd = '' #Execute this command to indicate DBA that
+ # master node has switched to sync mode.
+
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
# - Standby Servers -
*** a/src/include/replication/syncrep.h
--- b/src/include/replication/syncrep.h
***************
*** 33,38 ****
--- 33,41 ----
/* user-settable parameters for synchronous replication */
extern char *SyncRepStandbyNames;
+ extern char *master_to_standalone_cmd;
+ extern char *master_to_sync_cmd;
+ extern bool enable_standalone_master;
/* called by user backend */
extern void SyncRepWaitForLSN(XLogRecPtr XactCommitLSN);
***************
*** 53,56 **** extern int SyncRepWakeQueue(bool all, int mode);
--- 56,66 ----
extern bool check_synchronous_standby_names(char **newval, void **extra, GucSource source);
extern void assign_synchronous_commit(int newval, void *extra);
+ /* called to set the shared memory values*/
+ void SyncRepUpdateSyncStandaloneMasterAllowed(void);
+
+ /* called to check if any synchronous standby is alive*/
+ void SyncRepCheckSyncStdbyAlive(void);
+
+
#endif /* _SYNCREP_H */
*** a/src/include/replication/walsender_private.h
--- b/src/include/replication/walsender_private.h
***************
*** 88,93 **** typedef struct
--- 88,107 ----
*/
bool sync_standbys_defined;
+ /*
+ * Indicates the current running mode of master node. If it is true means
+ * there is no synchronous standby available so it is running in standalone
+ * mode.
+ */
+ bool sync_master_in_standalone_mode;
+
+ /*
+ * Whether master is allowed to switch to standalone if no synchronous
+ * standbys are available. This is a copy of GUC variable.
+ */
+ bool sync_standbalone_master_allowed;
+
+
WalSnd walsnds[1]; /* VARIABLE LENGTH ARRAY */
} WalSndCtlData;