*** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** *** 2236,2245 **** include 'filename' ! ! replication_timeout (integer) ! replication_timeout configuration parameter --- 2236,2245 ---- ! ! wal_sender_timeout (integer) ! wal_sender_timeout configuration parameter *************** *** 2251,2262 **** include 'filename' the postgresql.conf file or on the server command line. The default value is 60 seconds. - - To prevent connections from being terminated prematurely, - - must be enabled on the standby, and its value must be less than the - value of replication_timeout. - --- 2251,2256 ---- *************** *** 2474,2484 **** include 'filename' the postgresql.conf file or on the server command line. The default value is 10 seconds. - - When is enabled on a sending server, - wal_receiver_status_interval must be enabled, and its value - must be less than the value of replication_timeout. - --- 2468,2473 ---- *************** *** 2507,2512 **** include 'filename' --- 2496,2520 ---- + + wal_receiver_timeout (integer) + + wal_receiver_timeout configuration parameter + + + + Terminate replication connections that are inactive longer + than the specified number of milliseconds. This is useful for + the receiving standby server to detect a primary node crash or network + outage. + A value of zero disables the timeout mechanism. This parameter + can only be set in + the postgresql.conf file or on the server command line. + The default value is 60 seconds. + + + + *** a/doc/src/sgml/release-9.1.sgml --- b/doc/src/sgml/release-9.1.sgml *************** *** 3322,3328 **** Add ! replication_timeout setting (Fujii Masao, Heikki Linnakangas) --- 3322,3328 ---- Add ! replication_timeout setting (Fujii Masao, Heikki Linnakangas) *** a/src/backend/replication/walreceiver.c --- b/src/backend/replication/walreceiver.c *************** *** 55,60 **** --- 55,61 ---- /* GUC variables */ int wal_receiver_status_interval; + int wal_receiver_timeout; bool hot_standby_feedback; /* libpqreceiver hooks to these when loaded */ *************** *** 121,127 **** static void WalRcvDie(int code, Datum arg); static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len); static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr); static void XLogWalRcvFlush(bool dying); ! static void XLogWalRcvSendReply(void); static void XLogWalRcvSendHSFeedback(void); static void ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime); --- 122,128 ---- static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len); static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr); static void XLogWalRcvFlush(bool dying); ! static void XLogWalRcvSendReply(bool force, bool requestReply); static void XLogWalRcvSendHSFeedback(void); static void ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime); *************** *** 170,178 **** WalReceiverMain(void) { char conninfo[MAXCONNINFO]; XLogRecPtr startpoint; - /* use volatile pointer to prevent code rearrangement */ volatile WalRcvData *walrcv = WalRcv; /* * WalRcv should be set up already (if we are a backend, we inherit this --- 171,180 ---- { char conninfo[MAXCONNINFO]; XLogRecPtr startpoint; /* use volatile pointer to prevent code rearrangement */ volatile WalRcvData *walrcv = WalRcv; + TimestampTz last_recv_timestamp; + bool ping_sent; /* * WalRcv should be set up already (if we are a backend, we inherit this *************** *** 282,287 **** WalReceiverMain(void) --- 284,293 ---- MemSet(&reply_message, 0, sizeof(reply_message)); MemSet(&feedback_message, 0, sizeof(feedback_message)); + /* Initialize the last recv timestamp */ + last_recv_timestamp = GetCurrentTimestamp(); + ping_sent = false; + /* Loop until end-of-streaming or error */ for (;;) { *************** *** 316,330 **** WalReceiverMain(void) /* Wait a while for data to arrive */ if (walrcv_receive(NAPTIME_PER_CYCLE, &type, &buf, &len)) { /* Accept the received data, and process it */ XLogWalRcvProcessMsg(type, buf, len); /* Receive any more data we can without sleeping */ while (walrcv_receive(0, &type, &buf, &len)) XLogWalRcvProcessMsg(type, buf, len); /* Let the master know that we received some data. */ ! XLogWalRcvSendReply(); /* * If we've written some records, flush them to disk and let the --- 322,344 ---- /* Wait a while for data to arrive */ if (walrcv_receive(NAPTIME_PER_CYCLE, &type, &buf, &len)) { + /* Something was received from master, so reset timeout */ + last_recv_timestamp = GetCurrentTimestamp(); + ping_sent = false; + /* Accept the received data, and process it */ XLogWalRcvProcessMsg(type, buf, len); /* Receive any more data we can without sleeping */ while (walrcv_receive(0, &type, &buf, &len)) + { + last_recv_timestamp = GetCurrentTimestamp(); + ping_sent = false; XLogWalRcvProcessMsg(type, buf, len); + } /* Let the master know that we received some data. */ ! XLogWalRcvSendReply(false, false); /* * If we've written some records, flush them to disk and let the *************** *** 335,344 **** WalReceiverMain(void) else { /* ! * We didn't receive anything new, but send a status update to the ! * master anyway, to report any progress in applying WAL. */ ! XLogWalRcvSendReply(); XLogWalRcvSendHSFeedback(); } } --- 349,396 ---- else { /* ! * We didn't receive anything new. If we haven't heard anything ! * from the server for more than wal_receiver_timeout / 2, ! * ping the server. Also, if it's been longer than ! * wal_receiver_status_interval since the last update we sent, ! * send a status update to the master anyway, to report any ! * progress in applying WAL. ! */ ! bool requestReply = false; ! ! /* ! * Check if time since last receive from standby has reached the ! * configured limit. */ ! if (wal_receiver_timeout > 0) ! { ! TimestampTz now = GetCurrentTimestamp(); ! TimestampTz timeout; ! ! timeout = TimestampTzPlusMilliseconds(last_recv_timestamp, ! wal_receiver_timeout); ! ! if (now >= timeout) ! ereport(ERROR, ! (errmsg("terminating walreceiver due to timeout"))); ! ! /* ! * We didn't receive anything new, for half of receiver ! * replication timeout. Ping the server. ! */ ! if (!ping_sent) ! { ! timeout = TimestampTzPlusMilliseconds(last_recv_timestamp, ! (wal_receiver_timeout/2)); ! if (now >= timeout) ! { ! requestReply = true; ! ping_sent = true; ! } ! } ! } ! ! XLogWalRcvSendReply(requestReply, requestReply); XLogWalRcvSendHSFeedback(); } } *************** *** 460,465 **** XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len) --- 512,521 ---- memcpy(&keepalive, buf, sizeof(PrimaryKeepaliveMessage)); ProcessWalSndrMessage(keepalive.walEnd, keepalive.sendTime); + + /* If the primary requested a reply, send one immediately */ + if (keepalive.replyRequested) + XLogWalRcvSendReply(true, false); break; } default: *************** *** 609,627 **** XLogWalRcvFlush(bool dying) /* Also let the master know that we made some progress */ if (!dying) ! { ! XLogWalRcvSendReply(); ! XLogWalRcvSendHSFeedback(); ! } } } /* ! * Send reply message to primary, indicating our current XLOG positions and ! * the current time. */ static void ! XLogWalRcvSendReply(void) { char buf[sizeof(StandbyReplyMessage) + 1]; TimestampTz now; --- 665,688 ---- /* Also let the master know that we made some progress */ if (!dying) ! XLogWalRcvSendReply(false, false); } } /* ! * Send reply message to primary, indicating our current XLOG positions, oldest ! * xmin and the current time. ! * ! * If 'force' is not true, the message is not sent unless enough time has ! * passed since last status update to reach wal_receiver_status_internal (or ! * if wal_receiver_status_interval is disabled altogether). ! * ! * If 'requestReply' is true, requests the server to reply immediately upon ! * receiving this message. This is used for heartbearts, when approaching ! * wal_receiver_timeout. */ static void ! XLogWalRcvSendReply(bool force, bool requestReply) { char buf[sizeof(StandbyReplyMessage) + 1]; TimestampTz now; *************** *** 630,636 **** XLogWalRcvSendReply(void) * If the user doesn't want status to be reported to the master, be sure * to exit before doing anything at all. */ ! if (wal_receiver_status_interval <= 0) return; /* Get current timestamp. */ --- 691,697 ---- * If the user doesn't want status to be reported to the master, be sure * to exit before doing anything at all. */ ! if (!force && wal_receiver_status_interval <= 0) return; /* Get current timestamp. */ *************** *** 645,651 **** XLogWalRcvSendReply(void) * 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)) --- 706,713 ---- * this is only for reporting purposes and only on idle systems, that's * probably OK. */ ! if (!force ! && XLByteEQ(reply_message.write, LogstreamResult.Write) && XLByteEQ(reply_message.flush, LogstreamResult.Flush) && !TimestampDifferenceExceeds(reply_message.sendTime, now, wal_receiver_status_interval * 1000)) *************** *** 656,661 **** XLogWalRcvSendReply(void) --- 718,724 ---- reply_message.flush = LogstreamResult.Flush; reply_message.apply = GetXLogReplayRecPtr(NULL); reply_message.sendTime = now; + reply_message.replyRequested = requestReply; elog(DEBUG2, "sending write %X/%X flush %X/%X apply %X/%X", (uint32) (reply_message.write >> 32), (uint32) reply_message.write, *** a/src/backend/replication/walsender.c --- b/src/backend/replication/walsender.c *************** *** 82,88 **** static bool replication_started = false; /* Started streaming yet? */ /* User-settable parameters for walsender */ int max_wal_senders = 0; /* the maximum number of concurrent walsenders */ ! int replication_timeout = 60 * 1000; /* maximum time to send one * WAL data message */ /* * State for WalSndWakeupRequest --- 82,88 ---- /* User-settable parameters for walsender */ int max_wal_senders = 0; /* the maximum number of concurrent walsenders */ ! int wal_sender_timeout = 60 * 1000; /* maximum time to send one * WAL data message */ /* * State for WalSndWakeupRequest *************** *** 103,117 **** static uint32 sendOff = 0; */ static XLogRecPtr sentPtr = 0; /* ! * Buffer for processing reply messages. */ ! static StringInfoData reply_message; /* * Timestamp of the last receipt of the reply from the standby. */ static TimestampTz last_reply_timestamp; /* Flags set by signal handlers for later service in main loop */ static volatile sig_atomic_t got_SIGHUP = false; --- 103,122 ---- */ static XLogRecPtr sentPtr = 0; + /* Buffer for processing reply messages. */ + static StringInfoData reply_message; /* ! * Buffer for constructing outgoing messages ! * (1 + sizeof(WalDataMessageHeader) + MAX_SEND_SIZE bytes) */ ! static char *output_message; /* * Timestamp of the last receipt of the reply from the standby. */ static TimestampTz last_reply_timestamp; + /* Have we sent a heartbeat message asking for reply, since last reply? */ + static bool ping_sent = false; /* Flags set by signal handlers for later service in main loop */ static volatile sig_atomic_t got_SIGHUP = false; *************** *** 126,139 **** static void WalSndLastCycleHandler(SIGNAL_ARGS); static void WalSndLoop(void) __attribute__((noreturn)); static void InitWalSenderSlot(void); static void WalSndKill(int code, Datum arg); ! static void XLogSend(char *msgbuf, bool *caughtup); static void IdentifySystem(void); static void StartReplication(StartReplicationCmd *cmd); static void ProcessStandbyMessage(void); static void ProcessStandbyReplyMessage(void); static void ProcessStandbyHSFeedbackMessage(void); static void ProcessRepliesIfAny(void); ! static void WalSndKeepalive(char *msgbuf); /* Initialize walsender process before entering the main command loop */ --- 131,144 ---- static void WalSndLoop(void) __attribute__((noreturn)); static void InitWalSenderSlot(void); static void WalSndKill(int code, Datum arg); ! static void XLogSend(bool *caughtup); static void IdentifySystem(void); static void StartReplication(StartReplicationCmd *cmd); static void ProcessStandbyMessage(void); static void ProcessStandbyReplyMessage(void); static void ProcessStandbyHSFeedbackMessage(void); static void ProcessRepliesIfAny(void); ! static void WalSndKeepalive(bool requestReply); /* Initialize walsender process before entering the main command loop */ *************** *** 465,471 **** ProcessRepliesIfAny(void) --- 470,479 ---- * Save the last reply timestamp if we've received at least one reply. */ if (received) + { last_reply_timestamp = GetCurrentTimestamp(); + ping_sent = false; + } } /* *************** *** 527,532 **** ProcessStandbyReplyMessage(void) --- 535,544 ---- (uint32) (reply.flush >> 32), (uint32) reply.flush, (uint32) (reply.apply >> 32), (uint32) reply.apply); + /* Send a reply if the standby requested one. */ + if (reply.replyRequested) + WalSndKeepalive(false); + /* * Update shared state for this WalSender process based on reply data from * standby. *************** *** 620,626 **** ProcessStandbyHSFeedbackMessage(void) static void WalSndLoop(void) { - char *output_message; bool caughtup = false; /* --- 632,637 ---- *************** *** 638,643 **** WalSndLoop(void) --- 649,655 ---- /* Initialize the last reply timestamp */ last_reply_timestamp = GetCurrentTimestamp(); + ping_sent = false; /* Loop forever, unless we get an error */ for (;;) *************** *** 672,678 **** WalSndLoop(void) * caught up. */ if (!pq_is_send_pending()) ! XLogSend(output_message, &caughtup); else caughtup = false; --- 684,690 ---- * caught up. */ if (!pq_is_send_pending()) ! XLogSend(&caughtup); else caughtup = false; *************** *** 708,714 **** WalSndLoop(void) if (walsender_ready_to_stop) { /* ... let's just be real sure we're caught up ... */ ! XLogSend(output_message, &caughtup); if (caughtup && !pq_is_send_pending()) { /* Inform the standby that XLOG streaming is done */ --- 720,726 ---- if (walsender_ready_to_stop) { /* ... let's just be real sure we're caught up ... */ ! XLogSend(&caughtup); if (caughtup && !pq_is_send_pending()) { /* Inform the standby that XLOG streaming is done */ *************** *** 738,760 **** WalSndLoop(void) if (pq_is_send_pending()) wakeEvents |= WL_SOCKET_WRITEABLE; ! else if (MyWalSnd->sendKeepalive) { ! WalSndKeepalive(output_message); ! /* Try to flush pending output to the client */ ! if (pq_flush_if_writable() != 0) ! break; } /* Determine time until replication timeout */ ! if (replication_timeout > 0) { timeout = TimestampTzPlusMilliseconds(last_reply_timestamp, ! replication_timeout); ! sleeptime = 1 + (replication_timeout / 10); } ! /* Sleep until something happens or replication timeout */ ImmediateInterruptOK = true; CHECK_FOR_INTERRUPTS(); WaitLatchOrSocket(&MyWalSnd->latch, wakeEvents, --- 750,783 ---- if (pq_is_send_pending()) wakeEvents |= WL_SOCKET_WRITEABLE; ! else if (wal_sender_timeout > 0 && !ping_sent) { ! /* ! * If half of wal_sender_timeout has lapsed without receiving ! * any reply from standby, send a keep-alive message to standby ! * requesting an immediate reply. ! */ ! timeout = TimestampTzPlusMilliseconds(last_reply_timestamp, ! wal_sender_timeout / 2); ! if (GetCurrentTimestamp() >= timeout) ! { ! WalSndKeepalive(true); ! ping_sent = true; ! /* Try to flush pending output to the client */ ! if (pq_flush_if_writable() != 0) ! break; ! } } /* Determine time until replication timeout */ ! if (wal_sender_timeout > 0) { timeout = TimestampTzPlusMilliseconds(last_reply_timestamp, ! wal_sender_timeout); ! sleeptime = 1 + (wal_sender_timeout / 10); } ! /* Sleep until something happens or we time out */ ImmediateInterruptOK = true; CHECK_FOR_INTERRUPTS(); WaitLatchOrSocket(&MyWalSnd->latch, wakeEvents, *************** *** 766,773 **** WalSndLoop(void) * possibility that the client replied just as we reached the * timeout ... he's supposed to reply *before* that. */ ! if (replication_timeout > 0 && ! GetCurrentTimestamp() >= timeout) { /* * Since typically expiration of replication timeout means --- 789,795 ---- * possibility that the client replied just as we reached the * timeout ... he's supposed to reply *before* that. */ ! if (wal_sender_timeout > 0 && GetCurrentTimestamp() >= timeout) { /* * Since typically expiration of replication timeout means *************** *** 1016,1030 **** retry: * but not yet sent to the client, and buffer it in the libpq output * buffer. * - * msgbuf is a work area in which the output message is constructed. It's - * passed in just so we can avoid re-palloc'ing the buffer on each cycle. - * It must be of size 1 + sizeof(WalDataMessageHeader) + MAX_SEND_SIZE. - * * If there is no unsent WAL remaining, *caughtup is set to true, otherwise * *caughtup is set to false. */ static void ! XLogSend(char *msgbuf, bool *caughtup) { XLogRecPtr SendRqstPtr; XLogRecPtr startptr; --- 1038,1048 ---- * but not yet sent to the client, and buffer it in the libpq output * buffer. * * If there is no unsent WAL remaining, *caughtup is set to true, otherwise * *caughtup is set to false. */ static void ! XLogSend(bool *caughtup) { XLogRecPtr SendRqstPtr; XLogRecPtr startptr; *************** *** 1107,1119 **** XLogSend(char *msgbuf, bool *caughtup) /* * OK to read and send the slice. */ ! msgbuf[0] = 'w'; /* * Read the log directly into the output buffer to avoid extra memcpy * calls. */ ! XLogRead(msgbuf + 1 + sizeof(WalDataMessageHeader), startptr, nbytes); /* * We fill the message header last so that the send timestamp is taken as --- 1125,1137 ---- /* * OK to read and send the slice. */ ! output_message[0] = 'w'; /* * Read the log directly into the output buffer to avoid extra memcpy * calls. */ ! XLogRead(output_message + 1 + sizeof(WalDataMessageHeader), startptr, nbytes); /* * We fill the message header last so that the send timestamp is taken as *************** *** 1123,1131 **** XLogSend(char *msgbuf, bool *caughtup) msghdr.walEnd = SendRqstPtr; msghdr.sendTime = GetCurrentTimestamp(); ! memcpy(msgbuf + 1, &msghdr, sizeof(WalDataMessageHeader)); ! pq_putmessage_noblock('d', msgbuf, 1 + sizeof(WalDataMessageHeader) + nbytes); sentPtr = endptr; --- 1141,1149 ---- msghdr.walEnd = SendRqstPtr; msghdr.sendTime = GetCurrentTimestamp(); ! memcpy(output_message + 1, &msghdr, sizeof(WalDataMessageHeader)); ! pq_putmessage_noblock('d', output_message, 1 + sizeof(WalDataMessageHeader) + nbytes); sentPtr = endptr; *************** *** 1492,1512 **** pg_stat_get_wal_senders(PG_FUNCTION_ARGS) return (Datum) 0; } static void ! WalSndKeepalive(char *msgbuf) { PrimaryKeepaliveMessage keepalive_message; /* Construct a new message */ keepalive_message.walEnd = sentPtr; keepalive_message.sendTime = GetCurrentTimestamp(); elog(DEBUG2, "sending replication keepalive"); /* Prepend with the message type and send it. */ ! msgbuf[0] = 'k'; ! memcpy(msgbuf + 1, &keepalive_message, sizeof(PrimaryKeepaliveMessage)); ! pq_putmessage_noblock('d', msgbuf, sizeof(PrimaryKeepaliveMessage) + 1); } /* --- 1510,1536 ---- return (Datum) 0; } + /* + * This function is used to send keepalive message to standby. + * If requestReply is set, sets a flag in the message requesting the standby + * to send a message back to us, for heartbeat purposes. + */ static void ! WalSndKeepalive(bool requestReply) { PrimaryKeepaliveMessage keepalive_message; /* Construct a new message */ keepalive_message.walEnd = sentPtr; keepalive_message.sendTime = GetCurrentTimestamp(); + keepalive_message.replyRequested = requestReply; elog(DEBUG2, "sending replication keepalive"); /* Prepend with the message type and send it. */ ! output_message[0] = 'k'; ! memcpy(output_message + 1, &keepalive_message, sizeof(PrimaryKeepaliveMessage)); ! pq_putmessage_noblock('d', output_message, sizeof(PrimaryKeepaliveMessage) + 1); } /* *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 1596,1601 **** static struct config_int ConfigureNamesInt[] = --- 1596,1612 ---- }, { + {"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY, + gettext_noop("Sets the maximum wait time to receive data from master."), + NULL, + GUC_UNIT_MS + }, + &wal_receiver_timeout, + 60 * 1000, 0, INT_MAX, + NULL, NULL, NULL + }, + + { {"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS, gettext_noop("Sets the maximum number of concurrent connections."), NULL *************** *** 2019,2030 **** static struct config_int ConfigureNamesInt[] = }, { ! {"replication_timeout", PGC_SIGHUP, REPLICATION_SENDING, gettext_noop("Sets the maximum time to wait for WAL replication."), NULL, GUC_UNIT_MS }, ! &replication_timeout, 60 * 1000, 0, INT_MAX, NULL, NULL, NULL }, --- 2030,2041 ---- }, { ! {"wal_sender_timeout", PGC_SIGHUP, REPLICATION_SENDING, gettext_noop("Sets the maximum time to wait for WAL replication."), NULL, GUC_UNIT_MS }, ! &wal_sender_timeout, 60 * 1000, 0, INT_MAX, NULL, NULL, NULL }, *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 210,216 **** #max_wal_senders = 0 # max number of walsender processes # (change requires restart) #wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables ! #replication_timeout = 60s # in milliseconds; 0 disables # - Master Server - --- 210,216 ---- #max_wal_senders = 0 # max number of walsender processes # (change requires restart) #wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables ! #wal_sender_timeout = 60s # in milliseconds; 0 disables # - Master Server - *************** *** 237,242 **** --- 237,245 ---- # 0 disables #hot_standby_feedback = off # send info from standby to prevent # query conflicts + #wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables #------------------------------------------------------------------------------ *** a/src/include/replication/walprotocol.h --- b/src/include/replication/walprotocol.h *************** *** 27,32 **** typedef struct --- 27,38 ---- /* Sender's system clock at the time of transmission */ TimestampTz sendTime; + + /* + * If replyRequested is set, the client should reply immediately to this + * message, to avoid a timeout disconnect. + */ + bool replyRequested; } WalSndrMessage; *************** *** 80,85 **** typedef struct --- 86,97 ---- /* Sender's system clock at the time of transmission */ TimestampTz sendTime; + + /* + * If replyRequested is set, the server should reply immediately to this + * message, to avoid a timeout disconnect. + */ + bool replyRequested; } StandbyReplyMessage; /* *** a/src/include/replication/walreceiver.h --- b/src/include/replication/walreceiver.h *************** *** 17,23 **** --- 17,25 ---- #include "storage/spin.h" #include "pgtime.h" + /* user-settable parameters */ extern int wal_receiver_status_interval; + extern int wal_receiver_timeout; extern bool hot_standby_feedback; /* *** a/src/include/replication/walsender.h --- b/src/include/replication/walsender.h *************** *** 24,30 **** extern bool wake_wal_senders; /* user-settable parameters */ extern int max_wal_senders; ! extern int replication_timeout; extern void InitWalSender(void); extern void exec_replication_command(const char *query_string); --- 24,30 ---- /* user-settable parameters */ extern int max_wal_senders; ! extern int wal_sender_timeout; extern void InitWalSender(void); extern void exec_replication_command(const char *query_string); *** a/src/include/replication/walsender_private.h --- b/src/include/replication/walsender_private.h *************** *** 37,43 **** typedef struct WalSnd XLogRecPtr sentPtr; /* WAL has been sent up to this point */ bool needreload; /* does currently-open file need to be * reloaded? */ - bool sendKeepalive; /* do we send keepalives on this connection? */ /* * The xlog locations that have been written, flushed, and applied by --- 37,42 ----