*** 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;