*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 1505,1512 **** SET ENABLE_SEQSCAN TO OFF;
! Specifies whether transaction commit will wait for WAL records
! to be written to disk before the command returns a success>
indication to the client. The default, and safe, setting is
on>. When off>, there can be a delay between
when success is reported to the client and when the transaction is
--- 1505,1512 ----
! Specifies the level of durability for transaction commit, defining
! what actions must take place before the command returns a success>
indication to the client. The default, and safe, setting is
on>. When off>, there can be a delay between
when success is reported to the client and when the transaction is
***************
*** 1523,1528 **** SET ENABLE_SEQSCAN TO OFF;
--- 1523,1558 ----
discussion see .
+ In addition to on> and off> it is possible
+ to specify one of five different levels of durability.
+ The allowed values of synchronous_replication> are
+ off> do not wait for a local flush to disk.
+ on> wait for flush to disk, as desribed above.
+ memory> is a synonym for off>.
+ disk> is a synonym for on>.
+ replica-memory> commit waits for standby to receive into memory (no sync),
+ replica-disk> commit waits for sync to disk on standby,
+ replica-visible> commit waits for standby to apply changes so that they
+ will be visible to queries on standby server.
+
+
+ Specifying replica-memory>, replica-disk> or
+ replica-visible> means that there will be a delay while
+ the clients waits for confirmation of successful replication. That
+ delay will increase depending upon the physical distance and network
+ activity between primary and standby. The commit wait will last until a
+ reply from the current synchronous standby indicates the requested
+ level of confirmation. replica-disk> is safest, while
+ replica-memory> can be a little faster.
+ replica-visible> allows a more consistent cluster
+ from the application perspective and is useful for load balanced
+ clsuters.
+ The replica settings of this parameter have no effect if
+ is empty or
+ is zero. In that case the
+ parameter is taken to mean the same as disk>.
+
+
This parameter can be changed at any time; the behavior for any
one transaction is determined by the setting in effect when it
commits. It is therefore possible, and useful, to have some
***************
*** 2033,2071 **** SET ENABLE_SEQSCAN TO OFF;
-
- synchronous_replication (boolean)
-
- synchronous_replication> configuration parameter
-
-
-
- Specifies whether transaction commit will wait for WAL records
- to be replicated before the command returns a success>
- indication to the client. The default setting is off>.
- When on>, there will be a delay while the client waits
- for confirmation of successful replication. That delay will
- increase depending upon the physical distance and network activity
- between primary and standby. The commit wait will last until a
- reply from the current synchronous standby indicates it has written
- the commit record of the transaction to durable storage. This
- parameter has no effect if
- is empty or
- is zero.
-
-
- This parameter can be changed at any time; the
- behavior for any one transaction is determined by the setting in
- effect when it commits. It is therefore possible, and useful, to have
- some transactions replicate synchronously and others asynchronously.
- For example, to make a single multistatement transaction commit
- asynchronously when the default is synchronous replication, issue
- SET LOCAL synchronous_replication TO OFF> within the
- transaction.
-
-
-
-
synchronous_standby_names (string)
--- 2063,2068 ----
***************
*** 2100,2106 **** SET ENABLE_SEQSCAN TO OFF;
If a standby is removed from the list of servers then it will stop
being the synchronous standby, allowing another to take its place.
If the list is empty, synchronous replication will not be
! possible, whatever the setting of synchronous_replication>.
Standbys may also be added to the list without restarting the server.
--- 2097,2103 ----
If a standby is removed from the list of servers then it will stop
being the synchronous standby, allowing another to take its place.
If the list is empty, synchronous replication will not be
! possible, whatever the setting of synchronous_commit>.
Standbys may also be added to the list without restarting the server.
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 68,75 **** bool XactReadOnly;
bool DefaultXactDeferrable = false;
bool XactDeferrable;
- bool XactSyncCommit = true;
-
int CommitDelay = 0; /* precommit delay in microseconds */
int CommitSiblings = 5; /* # concurrent xacts needed to sleep */
--- 68,73 ----
***************
*** 1056,1062 **** RecordTransactionCommit(void)
* if all to-be-deleted tables are temporary though, since they are lost
* anyway if we crash.)
*/
! if ((wrote_xlog && XactSyncCommit) || forceSyncCommit || nrels > 0 || SyncRepRequested())
{
/*
* Synchronous commit case:
--- 1054,1061 ----
* if all to-be-deleted tables are temporary though, since they are lost
* anyway if we crash.)
*/
! if ((wrote_xlog && synchronous_commit >= SYNC_COMMIT_LOCAL_FLUSH) ||
! forceSyncCommit || nrels > 0)
{
/*
* Synchronous commit case:
*** a/src/backend/replication/syncrep.c
--- b/src/backend/replication/syncrep.c
***************
*** 63,69 ****
#include "utils/ps_status.h"
/* User-settable parameters for sync rep */
! bool synchronous_replication = false; /* Only set in user backends */
char *SyncRepStandbyNames;
#define SyncStandbysDefined() \
--- 63,69 ----
#include "utils/ps_status.h"
/* User-settable parameters for sync rep */
! int synchronous_commit = SYNC_COMMIT_LOCAL_FLUSH; /* Only set in user backends */
char *SyncRepStandbyNames;
#define SyncStandbysDefined() \
***************
*** 71,77 **** char *SyncRepStandbyNames;
static bool announce_next_takeover = true;
! static void SyncRepQueueInsert(void);
static void SyncRepCancelWait(void);
static int SyncRepGetStandbyPriority(void);
--- 71,77 ----
static bool announce_next_takeover = true;
! static void SyncRepQueueInsert(int queue);
static void SyncRepCancelWait(void);
static int SyncRepGetStandbyPriority(void);
***************
*** 105,111 **** SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
* there are no sync replication standby names defined.
* Note that those standbys don't need to be connected.
*/
! if (!SyncRepRequested() || !SyncStandbysDefined())
return;
Assert(SHMQueueIsDetached(&(MyProc->syncRepLinks)));
--- 105,112 ----
* there are no sync replication standby names defined.
* Note that those standbys don't need to be connected.
*/
! if (!SyncStandbysDefined() || max_wal_senders == 0 ||
! synchronous_commit < SYNC_COMMIT_REMOTE_WRITE)
return;
Assert(SHMQueueIsDetached(&(MyProc->syncRepLinks)));
***************
*** 126,132 **** SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
* so its likely to be a low cost check.
*/
if (!WalSndCtl->sync_standbys_defined ||
! XLByteLE(XactCommitLSN, WalSndCtl->lsn))
{
LWLockRelease(SyncRepLock);
return;
--- 127,133 ----
* so its likely to be a low cost check.
*/
if (!WalSndCtl->sync_standbys_defined ||
! XLByteLE(XactCommitLSN, WalSndCtl->lsn[synchronous_commit]))
{
LWLockRelease(SyncRepLock);
return;
***************
*** 138,144 **** SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
*/
MyProc->waitLSN = XactCommitLSN;
MyProc->syncRepState = SYNC_REP_WAITING;
! SyncRepQueueInsert();
Assert(SyncRepQueueIsOrderedByLSN());
LWLockRelease(SyncRepLock);
--- 139,145 ----
*/
MyProc->waitLSN = XactCommitLSN;
MyProc->syncRepState = SYNC_REP_WAITING;
! SyncRepQueueInsert(synchronous_commit);
Assert(SyncRepQueueIsOrderedByLSN());
LWLockRelease(SyncRepLock);
***************
*** 273,284 **** SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
* here out of order, so start at tail and work back to insertion point.
*/
static void
! SyncRepQueueInsert(void)
{
PGPROC *proc;
! proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue),
! &(WalSndCtl->SyncRepQueue),
offsetof(PGPROC, syncRepLinks));
while (proc)
--- 274,285 ----
* here out of order, so start at tail and work back to insertion point.
*/
static void
! SyncRepQueueInsert(int queue)
{
PGPROC *proc;
! proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue[queue]),
! &(WalSndCtl->SyncRepQueue[queue]),
offsetof(PGPROC, syncRepLinks));
while (proc)
***************
*** 290,296 **** SyncRepQueueInsert(void)
if (XLByteLT(proc->waitLSN, MyProc->waitLSN))
break;
! proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue),
&(proc->syncRepLinks),
offsetof(PGPROC, syncRepLinks));
}
--- 291,297 ----
if (XLByteLT(proc->waitLSN, MyProc->waitLSN))
break;
! proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue[queue]),
&(proc->syncRepLinks),
offsetof(PGPROC, syncRepLinks));
}
***************
*** 298,304 **** SyncRepQueueInsert(void)
if (proc)
SHMQueueInsertAfter(&(proc->syncRepLinks), &(MyProc->syncRepLinks));
else
! SHMQueueInsertAfter(&(WalSndCtl->SyncRepQueue), &(MyProc->syncRepLinks));
}
/*
--- 299,305 ----
if (proc)
SHMQueueInsertAfter(&(proc->syncRepLinks), &(MyProc->syncRepLinks));
else
! SHMQueueInsertAfter(&(WalSndCtl->SyncRepQueue[queue]), &(MyProc->syncRepLinks));
}
/*
***************
*** 421,442 **** SyncRepReleaseWaiters(void)
return;
}
! if (XLByteLT(walsndctl->lsn, MyWalSnd->flush))
! {
! /*
! * Set the lsn first so that when we wake backends they will
! * release up to this location.
! */
! walsndctl->lsn = MyWalSnd->flush;
! numprocs = SyncRepWakeQueue(false);
! }
! LWLockRelease(SyncRepLock);
! elog(DEBUG3, "released %d procs up to %X/%X",
! numprocs,
! MyWalSnd->flush.xlogid,
! MyWalSnd->flush.xrecoff);
/*
* If we are managing the highest priority standby, though we weren't
--- 422,441 ----
return;
}
! /*
! * Set the lsn first so that when we wake backends they will
! * release up to this location.
! */
! if (XLByteLT(walsndctl->lsn[0], MyWalSnd->write))
! walsndctl->lsn[0] = MyWalSnd->write;
! if (XLByteLT(walsndctl->lsn[1], MyWalSnd->flush))
! walsndctl->lsn[1] = MyWalSnd->flush;
! if (XLByteLT(walsndctl->lsn[2], MyWalSnd->apply))
! walsndctl->lsn[2] = MyWalSnd->apply;
! numprocs = SyncRepWakeQueue(false);
! LWLockRelease(SyncRepLock);
/*
* If we are managing the highest priority standby, though we weren't
***************
*** 515,563 **** SyncRepWakeQueue(bool all)
PGPROC *proc = NULL;
PGPROC *thisproc = NULL;
int numprocs = 0;
Assert(SyncRepQueueIsOrderedByLSN());
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue),
! &(WalSndCtl->SyncRepQueue),
! offsetof(PGPROC, syncRepLinks));
!
! while (proc)
{
! /*
! * Assume the queue is ordered by LSN
! */
! if (!all && XLByteLT(walsndctl->lsn, proc->waitLSN))
! return numprocs;
!
! /*
! * Move to next proc, so we can delete thisproc from the queue.
! * thisproc is valid, proc may be NULL after this.
! */
! thisproc = proc;
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue),
! &(proc->syncRepLinks),
offsetof(PGPROC, syncRepLinks));
! /*
! * Set state to complete; see SyncRepWaitForLSN() for discussion
! * of the various states.
! */
! thisproc->syncRepState = SYNC_REP_WAIT_COMPLETE;
!
! /*
! * Remove thisproc from queue.
! */
! SHMQueueDelete(&(thisproc->syncRepLinks));
!
! /*
! * Wake only when we have set state and removed from queue.
! */
! Assert(SHMQueueIsDetached(&(thisproc->syncRepLinks)));
! Assert(thisproc->syncRepState == SYNC_REP_WAIT_COMPLETE);
! SetLatch(&(thisproc->waitLatch));
!
! numprocs++;
}
return numprocs;
--- 514,566 ----
PGPROC *proc = NULL;
PGPROC *thisproc = NULL;
int numprocs = 0;
+ int i;
Assert(SyncRepQueueIsOrderedByLSN());
! for (i = 0; i < MAX_SYNC_REP_QUEUES; i++)
{
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[i]),
! &(WalSndCtl->SyncRepQueue[i]),
offsetof(PGPROC, syncRepLinks));
! while (proc)
! {
! /*
! * Assume the queue is ordered by LSN
! */
! if (!all && XLByteLT(walsndctl->lsn[i], proc->waitLSN))
! return numprocs;
!
! /*
! * Move to next proc, so we can delete thisproc from the queue.
! * thisproc is valid, proc may be NULL after this.
! */
! thisproc = proc;
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[i]),
! &(proc->syncRepLinks),
! offsetof(PGPROC, syncRepLinks));
!
! /*
! * Set state to complete; see SyncRepWaitForLSN() for discussion
! * of the various states.
! */
! thisproc->syncRepState = SYNC_REP_WAIT_COMPLETE;
!
! /*
! * Remove thisproc from queue.
! */
! SHMQueueDelete(&(thisproc->syncRepLinks));
!
! /*
! * Wake only when we have set state and removed from queue.
! */
! Assert(SHMQueueIsDetached(&(thisproc->syncRepLinks)));
! Assert(thisproc->syncRepState == SYNC_REP_WAIT_COMPLETE);
! SetLatch(&(thisproc->waitLatch));
!
! numprocs++;
! }
}
return numprocs;
***************
*** 606,633 **** SyncRepQueueIsOrderedByLSN(void)
{
PGPROC *proc = NULL;
XLogRecPtr lastLSN;
! lastLSN.xlogid = 0;
! lastLSN.xrecoff = 0;
!
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue),
! &(WalSndCtl->SyncRepQueue),
! offsetof(PGPROC, syncRepLinks));
!
! while (proc)
{
! /*
! * Check the queue is ordered by LSN and that multiple
! * procs don't have matching LSNs
! */
! if (XLByteLE(proc->waitLSN, lastLSN))
! return false;
!
! lastLSN = proc->waitLSN;
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue),
! &(proc->syncRepLinks),
offsetof(PGPROC, syncRepLinks));
}
return true;
--- 609,640 ----
{
PGPROC *proc = NULL;
XLogRecPtr lastLSN;
+ int i;
! for (i = 0; i < MAX_SYNC_REP_QUEUES; i++)
{
! lastLSN.xlogid = 0;
! lastLSN.xrecoff = 0;
! proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[i]),
! &(WalSndCtl->SyncRepQueue[i]),
offsetof(PGPROC, syncRepLinks));
+
+ while (proc)
+ {
+ /*
+ * Check the queue is ordered by LSN and that multiple
+ * procs don't have matching LSNs
+ */
+ if (XLByteLE(proc->waitLSN, lastLSN))
+ return false;
+
+ lastLSN = proc->waitLSN;
+
+ proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[i]),
+ &(proc->syncRepLinks),
+ offsetof(PGPROC, syncRepLinks));
+ }
}
return true;
*** a/src/backend/replication/walreceiver.c
--- b/src/backend/replication/walreceiver.c
***************
*** 610,623 **** XLogWalRcvSendReply(void)
/*
* We can compare the write and flush positions to the last message we
* sent without taking any lock, but the apply position requires a spin
! * lock, so we don't check that unless something else has changed or 10
! * seconds have passed. This means that the apply log position will
! * appear, from the master's point of view, to lag slightly, but since
! * this is only for reporting purposes and only on idle systems, that's
! * probably OK.
*/
if (XLByteEQ(reply_message.write, LogstreamResult.Write)
&& XLByteEQ(reply_message.flush, LogstreamResult.Flush)
&& !TimestampDifferenceExceeds(reply_message.sendTime, now,
wal_receiver_status_interval * 1000))
return;
--- 610,621 ----
/*
* We can compare the write and flush positions to the last message we
* sent without taking any lock, but the apply position requires a spin
! * lock, so we just check whether the last message showed that all
! * flushed WAL data has been applied.
*/
if (XLByteEQ(reply_message.write, LogstreamResult.Write)
&& XLByteEQ(reply_message.flush, LogstreamResult.Flush)
+ && XLByteEQ(reply_message.flush, reply_message.apply)
&& !TimestampDifferenceExceeds(reply_message.sendTime, now,
wal_receiver_status_interval * 1000))
return;
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 1247,1253 **** WalSndShmemInit(void)
/* First time through, so initialize */
MemSet(WalSndCtl, 0, WalSndShmemSize());
! SHMQueueInit(&(WalSndCtl->SyncRepQueue));
for (i = 0; i < max_wal_senders; i++)
{
--- 1247,1256 ----
/* First time through, so initialize */
MemSet(WalSndCtl, 0, WalSndShmemSize());
! for (i = 0; i < MAX_SYNC_REP_QUEUES; i++)
! {
! SHMQueueInit(&(WalSndCtl->SyncRepQueue[i]));
! }
for (i = 0; i < max_wal_senders; i++)
{
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 747,768 **** static struct config_bool ConfigureNamesBool[] =
true, NULL, NULL
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
- gettext_noop("Sets immediate fsync at commit."),
- NULL
- },
- &XactSyncCommit,
- true, NULL, NULL
- },
- {
- {"synchronous_replication", PGC_USERSET, WAL_REPLICATION,
- gettext_noop("Requests synchronous replication."),
- NULL
- },
- &synchronous_replication,
- false, NULL, NULL
- },
- {
{"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Continues processing past damaged page headers."),
gettext_noop("Detection of a damaged page header normally causes PostgreSQL to "
--- 747,752 ----
***************
*** 2818,2823 **** static struct config_enum ConfigureNamesEnum[] =
--- 2802,2817 ----
},
{
+ {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ gettext_noop("Durability level requested at commit."),
+ NULL
+ },
+ &synchronous_commit,
+ SYNC_COMMIT_LOCAL_FLUSH, synchronous_commit_options,
+ NULL, NULL
+ },
+
+ {
{"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 153,159 ****
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
! #synchronous_commit = on # immediate fsync at commit
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
--- 153,167 ----
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
!
! #synchronous_commit = on # durability level requested
! # valid values: on, off, plus these values
! # memory = in local memory only, not on disk
! # disk = flushed to local disk only (same as "on")
! # replica-memory = in memory on standby
! # replica-disk = flushed to disk on standby
! # replica-visible = changes visible on standby
!
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
***************
*** 184,193 ****
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
- # - Replication - User Settings
-
- #synchronous_replication = off # does commit wait for reply from standby
-
# - Streaming Replication - Server Settings
#synchronous_standby_names = '' # standby servers that provide sync rep
--- 192,197 ----
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
***************
*** 52,60 **** extern bool XactReadOnly;
extern bool DefaultXactDeferrable;
extern bool XactDeferrable;
- /* Asynchronous commits */
- extern bool XactSyncCommit;
-
/* Kluge for 2PC support */
extern bool MyXactAccessedTempRel;
--- 52,57 ----
*** a/src/include/replication/syncrep.h
--- b/src/include/replication/syncrep.h
***************
*** 19,34 ****
#include "storage/spin.h"
#include "utils/guc.h"
- #define SyncRepRequested() \
- (synchronous_replication && max_wal_senders > 0)
-
/* syncRepState */
#define SYNC_REP_NOT_WAITING 0
#define SYNC_REP_WAITING 1
#define SYNC_REP_WAIT_COMPLETE 2
/* user-settable parameters for synchronous replication */
! extern bool synchronous_replication;
extern char *SyncRepStandbyNames;
/* called by user backend */
--- 19,63 ----
#include "storage/spin.h"
#include "utils/guc.h"
/* syncRepState */
#define SYNC_REP_NOT_WAITING 0
#define SYNC_REP_WAITING 1
#define SYNC_REP_WAIT_COMPLETE 2
+ typedef enum
+ {
+ SYNC_COMMIT_NO_LOCAL_FLUSH = -2, /* same as old sync_commit = off */
+ SYNC_COMMIT_LOCAL_FLUSH, /* default ACID behaviour */
+ SYNC_COMMIT_REMOTE_WRITE, /* sync rep, wait for write of data on standby */
+ SYNC_COMMIT_REMOTE_FLUSH, /* sync rep, wait for fsync of data on standby */
+ SYNC_COMMIT_REMOTE_APPLY /* sync rep, wait for apply of data on standby */
+ } SyncCommitWaitType;
+
+ /*
+ * Accept documented options, plus undocumented variations of "on" and "off".
+ */
+ static const struct config_enum_entry synchronous_commit_options[] = {
+ {"memory", SYNC_COMMIT_NO_LOCAL_FLUSH, false},
+ {"disk", SYNC_COMMIT_LOCAL_FLUSH, false},
+ {"replica-memory", SYNC_COMMIT_REMOTE_WRITE, false},
+ {"replica-disk", SYNC_COMMIT_REMOTE_FLUSH, false},
+ {"replica-visible", SYNC_COMMIT_REMOTE_APPLY, false},
+ {"on", SYNC_COMMIT_LOCAL_FLUSH, false},
+ {"off", SYNC_COMMIT_NO_LOCAL_FLUSH, false},
+ {"true", SYNC_COMMIT_LOCAL_FLUSH, true},
+ {"false", SYNC_COMMIT_NO_LOCAL_FLUSH, true},
+ {"yes", SYNC_COMMIT_LOCAL_FLUSH, true},
+ {"no", SYNC_COMMIT_NO_LOCAL_FLUSH, true},
+ {"1", SYNC_COMMIT_LOCAL_FLUSH, true},
+ {"0", SYNC_COMMIT_NO_LOCAL_FLUSH, true},
+ {NULL, 0, false}
+ };
+
+ /* sync replication has separate queues for write, fsync and apply */
+ #define MAX_SYNC_REP_QUEUES 3
+
/* user-settable parameters for synchronous replication */
! extern int synchronous_commit;
extern char *SyncRepStandbyNames;
/* called by user backend */
*** a/src/include/replication/walsender.h
--- b/src/include/replication/walsender.h
***************
*** 68,82 **** extern WalSnd *MyWalSnd;
typedef struct
{
/*
! * Synchronous replication queue. Protected by SyncRepLock.
*/
! SHM_QUEUE SyncRepQueue;
/*
* Current location of the head of the queue. All waiters should have
* a waitLSN that follows this value. Protected by SyncRepLock.
*/
! XLogRecPtr lsn;
/*
* Are any sync standbys defined? Waiting backends can't reload the
--- 68,82 ----
typedef struct
{
/*
! * Synchronous replication queues. Protected by SyncRepLock.
*/
! SHM_QUEUE SyncRepQueue[MAX_SYNC_REP_QUEUES];
/*
* Current location of the head of the queue. All waiters should have
* a waitLSN that follows this value. Protected by SyncRepLock.
*/
! XLogRecPtr lsn[MAX_SYNC_REP_QUEUES];
/*
* Are any sync standbys defined? Waiting backends can't reload the