Commit 06828c5f authored by Simon Riggs's avatar Simon Riggs

Separate messages for standby replies and hot standby feedback.

Allow messages to be sent at different times, and greatly reduce
the frequency of hot standby feedback. Refactor to allow additional
message types.
parent 45a6d79b
...@@ -95,6 +95,7 @@ static struct ...@@ -95,6 +95,7 @@ static struct
} LogstreamResult; } LogstreamResult;
static StandbyReplyMessage reply_message; static StandbyReplyMessage reply_message;
static StandbyHSFeedbackMessage feedback_message;
/* /*
* About SIGTERM handling: * About SIGTERM handling:
...@@ -123,6 +124,7 @@ static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len); ...@@ -123,6 +124,7 @@ static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len);
static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr); static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr);
static void XLogWalRcvFlush(bool dying); static void XLogWalRcvFlush(bool dying);
static void XLogWalRcvSendReply(void); static void XLogWalRcvSendReply(void);
static void XLogWalRcvSendHSFeedback(void);
/* Signal handlers */ /* Signal handlers */
static void WalRcvSigHupHandler(SIGNAL_ARGS); static void WalRcvSigHupHandler(SIGNAL_ARGS);
...@@ -317,6 +319,7 @@ WalReceiverMain(void) ...@@ -317,6 +319,7 @@ WalReceiverMain(void)
/* Let the master know that we received some data. */ /* Let the master know that we received some data. */
XLogWalRcvSendReply(); XLogWalRcvSendReply();
XLogWalRcvSendHSFeedback();
/* /*
* If we've written some records, flush them to disk and let the * If we've written some records, flush them to disk and let the
...@@ -331,6 +334,7 @@ WalReceiverMain(void) ...@@ -331,6 +334,7 @@ WalReceiverMain(void)
* the master anyway, to report any progress in applying WAL. * the master anyway, to report any progress in applying WAL.
*/ */
XLogWalRcvSendReply(); XLogWalRcvSendReply();
XLogWalRcvSendHSFeedback();
} }
} }
} }
...@@ -619,40 +623,82 @@ XLogWalRcvSendReply(void) ...@@ -619,40 +623,82 @@ XLogWalRcvSendReply(void)
reply_message.apply = GetXLogReplayRecPtr(); reply_message.apply = GetXLogReplayRecPtr();
reply_message.sendTime = now; reply_message.sendTime = now;
/* elog(DEBUG2, "sending write %X/%X flush %X/%X apply %X/%X",
* Get the OldestXmin and its associated epoch reply_message.write.xlogid, reply_message.write.xrecoff,
reply_message.flush.xlogid, reply_message.flush.xrecoff,
reply_message.apply.xlogid, reply_message.apply.xrecoff);
/* Prepend with the message type and send it. */
buf[0] = 'r';
memcpy(&buf[1], &reply_message, sizeof(StandbyReplyMessage));
walrcv_send(buf, sizeof(StandbyReplyMessage) + 1);
}
/*
* Send hot standby feedback message to primary, plus the current time,
* in case they don't have a watch.
*/ */
if (hot_standby_feedback && HotStandbyActive()) static void
{ XLogWalRcvSendHSFeedback(void)
{
char buf[sizeof(StandbyHSFeedbackMessage) + 1];
TimestampTz now;
TransactionId nextXid; TransactionId nextXid;
uint32 nextEpoch; uint32 nextEpoch;
TransactionId xmin;
/*
* If the user doesn't want status to be reported to the master, be sure
* to exit before doing anything at all.
*/
if (!hot_standby_feedback)
return;
reply_message.xmin = GetOldestXmin(true, false); /* Get current timestamp. */
now = GetCurrentTimestamp();
/*
* Send feedback at most once per wal_receiver_status_interval.
*/
if (!TimestampDifferenceExceeds(feedback_message.sendTime, now,
wal_receiver_status_interval * 1000))
return;
/*
* If Hot Standby is not yet active there is nothing to send.
* Check this after the interval has expired to reduce number of
* calls.
*/
if (!HotStandbyActive())
return;
/*
* Make the expensive call to get the oldest xmin once we are
* certain everything else has been checked.
*/
xmin = GetOldestXmin(true, false);
/* /*
* Get epoch and adjust if nextXid and oldestXmin are different * Get epoch and adjust if nextXid and oldestXmin are different
* sides of the epoch boundary. * sides of the epoch boundary.
*/ */
GetNextXidAndEpoch(&nextXid, &nextEpoch); GetNextXidAndEpoch(&nextXid, &nextEpoch);
if (nextXid < reply_message.xmin) if (nextXid < xmin)
nextEpoch--; nextEpoch--;
reply_message.epoch = nextEpoch;
}
else
{
reply_message.xmin = InvalidTransactionId;
reply_message.epoch = 0;
}
elog(DEBUG2, "sending write %X/%X flush %X/%X apply %X/%X xmin %u epoch %u", /*
reply_message.write.xlogid, reply_message.write.xrecoff, * Always send feedback message.
reply_message.flush.xlogid, reply_message.flush.xrecoff, */
reply_message.apply.xlogid, reply_message.apply.xrecoff, feedback_message.sendTime = now;
reply_message.xmin, feedback_message.xmin = xmin;
reply_message.epoch); feedback_message.epoch = nextEpoch;
elog(DEBUG2, "sending hot standby feedback xmin %u epoch %u",
feedback_message.xmin,
feedback_message.epoch);
/* Prepend with the message type and send it. */ /* Prepend with the message type and send it. */
buf[0] = 'r'; buf[0] = 'h';
memcpy(&buf[1], &reply_message, sizeof(StandbyReplyMessage)); memcpy(&buf[1], &feedback_message, sizeof(StandbyHSFeedbackMessage));
walrcv_send(buf, sizeof(StandbyReplyMessage) + 1); walrcv_send(buf, sizeof(StandbyHSFeedbackMessage) + 1);
} }
...@@ -116,7 +116,9 @@ static void WalSndKill(int code, Datum arg); ...@@ -116,7 +116,9 @@ static void WalSndKill(int code, Datum arg);
static bool XLogSend(char *msgbuf, bool *caughtup); static bool XLogSend(char *msgbuf, bool *caughtup);
static void IdentifySystem(void); static void IdentifySystem(void);
static void StartReplication(StartReplicationCmd * cmd); static void StartReplication(StartReplicationCmd * cmd);
static void ProcessStandbyMessage(void);
static void ProcessStandbyReplyMessage(void); static void ProcessStandbyReplyMessage(void);
static void ProcessStandbyHSFeedbackMessage(void);
static void ProcessRepliesIfAny(void); static void ProcessRepliesIfAny(void);
...@@ -456,6 +458,8 @@ ProcessRepliesIfAny(void) ...@@ -456,6 +458,8 @@ ProcessRepliesIfAny(void)
unsigned char firstchar; unsigned char firstchar;
int r; int r;
for (;;)
{
r = pq_getbyte_if_available(&firstchar); r = pq_getbyte_if_available(&firstchar);
if (r < 0) if (r < 0)
{ {
...@@ -478,7 +482,7 @@ ProcessRepliesIfAny(void) ...@@ -478,7 +482,7 @@ ProcessRepliesIfAny(void)
* 'd' means a standby reply wrapped in a CopyData packet. * 'd' means a standby reply wrapped in a CopyData packet.
*/ */
case 'd': case 'd':
ProcessStandbyReplyMessage(); ProcessStandbyMessage();
break; break;
/* /*
...@@ -493,17 +497,16 @@ ProcessRepliesIfAny(void) ...@@ -493,17 +497,16 @@ ProcessRepliesIfAny(void)
errmsg("invalid standby closing message type %d", errmsg("invalid standby closing message type %d",
firstchar))); firstchar)));
} }
}
} }
/* /*
* Process a status update message received from standby. * Process a status update message received from standby.
*/ */
static void static void
ProcessStandbyReplyMessage(void) ProcessStandbyMessage(void)
{ {
StandbyReplyMessage reply;
char msgtype; char msgtype;
TransactionId newxmin = InvalidTransactionId;
resetStringInfo(&reply_message); resetStringInfo(&reply_message);
...@@ -523,22 +526,39 @@ ProcessStandbyReplyMessage(void) ...@@ -523,22 +526,39 @@ ProcessStandbyReplyMessage(void)
* one type. * one type.
*/ */
msgtype = pq_getmsgbyte(&reply_message); msgtype = pq_getmsgbyte(&reply_message);
if (msgtype != 'r')
switch (msgtype)
{ {
case 'r':
ProcessStandbyReplyMessage();
break;
case 'h':
ProcessStandbyHSFeedbackMessage();
break;
default:
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("unexpected message type %c", msgtype))); errmsg("unexpected message type %c", msgtype)));
proc_exit(0); proc_exit(0);
} }
}
/*
* Regular reply from standby advising of WAL positions on standby server.
*/
static void
ProcessStandbyReplyMessage(void)
{
StandbyReplyMessage reply;
pq_copymsgbytes(&reply_message, (char *) &reply, sizeof(StandbyReplyMessage)); pq_copymsgbytes(&reply_message, (char *) &reply, sizeof(StandbyReplyMessage));
elog(DEBUG2, "write %X/%X flush %X/%X apply %X/%X xmin %u epoch %u", elog(DEBUG2, "write %X/%X flush %X/%X apply %X/%X",
reply.write.xlogid, reply.write.xrecoff, reply.write.xlogid, reply.write.xrecoff,
reply.flush.xlogid, reply.flush.xrecoff, reply.flush.xlogid, reply.flush.xrecoff,
reply.apply.xlogid, reply.apply.xrecoff, reply.apply.xlogid, reply.apply.xrecoff);
reply.xmin,
reply.epoch);
/* /*
* Update shared state for this WalSender process * Update shared state for this WalSender process
...@@ -554,6 +574,22 @@ ProcessStandbyReplyMessage(void) ...@@ -554,6 +574,22 @@ ProcessStandbyReplyMessage(void)
walsnd->apply = reply.apply; walsnd->apply = reply.apply;
SpinLockRelease(&walsnd->mutex); SpinLockRelease(&walsnd->mutex);
} }
}
/*
* Hot Standby feedback
*/
static void
ProcessStandbyHSFeedbackMessage(void)
{
StandbyHSFeedbackMessage reply;
TransactionId newxmin = InvalidTransactionId;
pq_copymsgbytes(&reply_message, (char *) &reply, sizeof(StandbyHSFeedbackMessage));
elog(DEBUG2, "hot standby feedback xmin %u epoch %u",
reply.xmin,
reply.epoch);
/* /*
* Update the WalSender's proc xmin to allow it to be visible * Update the WalSender's proc xmin to allow it to be visible
......
...@@ -56,6 +56,18 @@ typedef struct ...@@ -56,6 +56,18 @@ typedef struct
XLogRecPtr flush; XLogRecPtr flush;
XLogRecPtr apply; XLogRecPtr apply;
/* Sender's system clock at the time of transmission */
TimestampTz sendTime;
} StandbyReplyMessage;
/*
* Hot Standby feedback from standby (message type 'h'). This is wrapped within
* a CopyData message at the FE/BE protocol level.
*
* Note that the data length is not specified here.
*/
typedef struct
{
/* /*
* The current xmin and epoch from the standby, for Hot Standby feedback. * The current xmin and epoch from the standby, for Hot Standby feedback.
* This may be invalid if the standby-side does not support feedback, * This may be invalid if the standby-side does not support feedback,
...@@ -64,10 +76,9 @@ typedef struct ...@@ -64,10 +76,9 @@ typedef struct
TransactionId xmin; TransactionId xmin;
uint32 epoch; uint32 epoch;
/* Sender's system clock at the time of transmission */ /* Sender's system clock at the time of transmission */
TimestampTz sendTime; TimestampTz sendTime;
} StandbyReplyMessage; } StandbyHSFeedbackMessage;
/* /*
* Maximum data payload in a WAL data message. Must be >= XLOG_BLCKSZ. * Maximum data payload in a WAL data message. Must be >= XLOG_BLCKSZ.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment