sync-standalone-v3.patch
application/octet-stream
Filename: sync-standalone-v3.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 v3
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 6 | 1 |
| src/backend/postmaster/checkpointer.c | 3 | 3 |
| src/backend/replication/syncrep.c | 98 | 1 |
| src/backend/replication/walsender.c | 7 | 0 |
| src/backend/utils/misc/guc.c | 2 | 1 |
| src/backend/utils/misc/postgresql.conf.sample | 2 | 1 |
| src/include/access/xact.h | 1 | 0 |
| src/include/replication/syncrep.h | 3 | 1 |
| src/include/replication/walsender_private.h | 11 | 0 |
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0cc3296..60bebee 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1560,7 +1560,8 @@ SET ENABLE_SEQSCAN TO OFF;
Specifies whether transaction commit will wait for WAL records
to be written to disk before the command returns a <quote>success</>
indication to the client. Valid values are <literal>on</>,
- <literal>local</>, and <literal>off</>. The default, and safe, value
+ <literal>local</>, <literal>fallback</> and <literal>off</>.
+ The default, and safe, value
is <literal>on</>. When <literal>off</>, there can be a delay between
when success is reported to the client and when the transaction is
really guaranteed to be safe against a server crash. (The maximum
@@ -1574,6 +1575,10 @@ SET ENABLE_SEQSCAN TO OFF;
can be a useful alternative when performance is more important than
exact certainty about the durability of a transaction. For more
discussion see <xref linkend="wal-async-commit">.
+ If set to <literal>fallback</>, the master will act as if it was set to
+ <literal>on</> except in the special case where all suitable synchronous
+ standbys are currentlydisconnected, in which case it will temporarily fall
+ back to <literal>local</> mode until suitable standbys are connected.
</para>
<para>
If <xref linkend="guc-synchronous-standby-names"> is set, this
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index e9ae1e8..7c39bad 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -352,7 +352,7 @@ CheckpointerMain(void)
ThisTimeLineID = GetRecoveryTargetTLI();
/* Do this once before starting the loop, then just at SIGHUP time. */
- SyncRepUpdateSyncStandbysDefined();
+ SyncRepUpdateConfig();
/*
* Loop forever
@@ -381,7 +381,7 @@ CheckpointerMain(void)
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
/* update global shmem state for sync rep */
- SyncRepUpdateSyncStandbysDefined();
+ SyncRepUpdateConfig();
}
if (checkpoint_requested)
{
@@ -657,7 +657,7 @@ CheckpointWriteDelay(int flags, double progress)
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
/* update global shmem state for sync rep */
- SyncRepUpdateSyncStandbysDefined();
+ SyncRepUpdateConfig();
}
AbsorbFsyncRequests();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 95de6c7..559c89c 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -126,6 +126,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 +337,58 @@ 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.
+ */
+void SyncRepCheckIfStandaloneMaster()
+{
+ bool sync_standby_connected = false;
+ int i = 0;
+
+ if (!SyncRepRequested() || !SyncStandbysDefined() || ! WalSndCtl->sync_standalone_allowed)
+ {
+ WalSndCtl->sync_standalone_master = false;
+ return;
+ }
+
+ for (i = 0; i < max_wal_senders && ! sync_standby_connected; i++)
+ {
+ volatile WalSnd *walsnd = &WalSndCtl->walsnds[i];
+ if ( walsnd->pid != 0 && walsnd->sync_standby_priority )
+ {
+ sync_standby_connected = true;
+ if ( WalSndCtl->sync_standalone_master )
+ {
+ ereport(LOG,
+ (errmsg("waiting for standby synchronization"),
+ errhidestmt(true)));
+
+ WalSndCtl->sync_standalone_master = false;
+ }
+ }
+ }
+
+ if ( ! sync_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);
+ }
+ }
+}
+
+/*
* ===========================================================
* Synchronous Replication functions for wal sender processes
* ===========================================================
@@ -345,10 +408,11 @@ SyncRepInitConfig(void)
* for handling replies from standby.
*/
priority = SyncRepGetStandbyPriority();
- if (MyWalSnd->sync_standby_priority != priority)
+ if (priority)
{
LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
MyWalSnd->sync_standby_priority = priority;
+ SyncRepCheckIfStandaloneMaster();
LWLockRelease(SyncRepLock);
ereport(DEBUG1,
(errmsg("standby \"%s\" now has synchronous standby priority %u",
@@ -567,6 +631,17 @@ SyncRepWakeQueue(bool all)
}
/*
+ * The background writer calls this as needed to update the shared WalSndCtl
+ * with new sync-related config.
+ */
+void
+SyncRepUpdateConfig(void)
+{
+ SyncRepUpdateSyncStandbysDefined();
+ SyncRepUpdateSyncStandaloneAllowed();
+}
+
+/*
* The background writer calls this as needed to update the shared
* sync_standbys_defined flag, so that backends don't remain permanently wedged
* if synchronous_standby_names is unset. It's safe to check the current value
@@ -598,6 +673,28 @@ 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)
+{
+ bool SyncRepStandaloneMasterAllowed = SyncRepRequested() && synchronous_commit == SYNCHRONOUS_COMMIT_FALLBACK;
+ 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..f2d8f96 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -370,11 +370,12 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
};
/*
- * Although only "on", "off", and "local" are documented, we
+ * Although only "on", "off", "fallback" and "local" are documented, we
* accept all the likely variants of "on" and "off".
*/
static const struct config_enum_entry synchronous_commit_options[] = {
{"local", SYNCHRONOUS_COMMIT_LOCAL_FLUSH, false},
+ {"fallback", SYNCHRONOUS_COMMIT_FALLBACK, false},
{"on", SYNCHRONOUS_COMMIT_ON, false},
{"off", SYNCHRONOUS_COMMIT_OFF, false},
{"true", SYNCHRONOUS_COMMIT_ON, true},
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 315db46..83bc120 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -158,7 +158,7 @@
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
-#synchronous_commit = on # synchronization level; on, off, or local
+#synchronous_commit = on # synchronization level; on, off, fallback or local
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
@@ -215,6 +215,7 @@
# from standby(s); '*' = all
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+
# - Standby Servers -
# These settings are ignored on a master server
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index aaa6204..6e8f7dc 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -55,6 +55,7 @@ typedef enum
{
SYNCHRONOUS_COMMIT_OFF, /* asynchronous commit */
SYNCHRONOUS_COMMIT_LOCAL_FLUSH, /* wait for local flush only */
+ SYNCHRONOUS_COMMIT_FALLBACK, /* wait for local and remote flush, if connected standbys */
SYNCHRONOUS_COMMIT_REMOTE_FLUSH /* wait for local and remote flush */
} SyncCommitLevel;
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index 65b725f..66a2c71 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -34,7 +34,9 @@ extern void SyncRepInitConfig(void);
extern void SyncRepReleaseWaiters(void);
/* called by wal writer */
-extern void SyncRepUpdateSyncStandbysDefined(void);
+extern void SyncRepUpdateSyncConfig(void);
+
+extern void 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;