Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e9d4aa5

Browse files
committed
Fix walsender to exit promptly if client requests shutdown.
It's possible for WalSndWaitForWal to be asked to wait for WAL that doesn't exist yet. That's fine, in fact it's the normal situation if we're caught up; but when the client requests shutdown we should not keep waiting. The previous coding could wait indefinitely if the source server was idle. In passing, improve the rather weak comments in this area, and slightly rearrange some related code for better readability. Back-patch to 9.4 where this code was introduced. Discussion: https://postgr.es/m/14154.1498781234@sss.pgh.pa.us
1 parent 43c67e3 commit e9d4aa5

File tree

1 file changed

+31
-17
lines changed

1 file changed

+31
-17
lines changed

src/backend/replication/walsender.c

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -764,15 +764,14 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
764764
/* make sure we have enough WAL available */
765765
flushptr = WalSndWaitForWal(targetPagePtr + reqLen);
766766

767-
/* more than one block available */
768-
if (targetPagePtr + XLOG_BLCKSZ <= flushptr)
769-
count = XLOG_BLCKSZ;
770-
/* not enough WAL synced, that can happen during shutdown */
771-
else if (targetPagePtr + reqLen > flushptr)
767+
/* fail if not (implies we are going to shut down) */
768+
if (flushptr < targetPagePtr + reqLen)
772769
return -1;
773-
/* part of the page available */
770+
771+
if (targetPagePtr + XLOG_BLCKSZ <= flushptr)
772+
count = XLOG_BLCKSZ; /* more than one block available */
774773
else
775-
count = flushptr - targetPagePtr;
774+
count = flushptr - targetPagePtr; /* part of the page available */
776775

777776
/* now actually read the data, we know it's there */
778777
XLogRead(cur_page, targetPagePtr, XLOG_BLCKSZ);
@@ -1158,7 +1157,11 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
11581157
}
11591158

11601159
/*
1161-
* Wait till WAL < loc is flushed to disk so it can be safely read.
1160+
* Wait till WAL < loc is flushed to disk so it can be safely sent to client.
1161+
*
1162+
* Returns end LSN of flushed WAL. Normally this will be >= loc, but
1163+
* if we detect a shutdown request (either from postmaster or client)
1164+
* we will return early, so caller must always check.
11621165
*/
11631166
static XLogRecPtr
11641167
WalSndWaitForWal(XLogRecPtr loc)
@@ -1225,9 +1228,7 @@ WalSndWaitForWal(XLogRecPtr loc)
12251228
RecentFlushPtr = GetXLogReplayRecPtr(NULL);
12261229

12271230
/*
1228-
* If postmaster asked us to stop, don't wait here anymore. This will
1229-
* cause the xlogreader to return without reading a full record, which
1230-
* is the fastest way to reach the mainloop which then can quit.
1231+
* If postmaster asked us to stop, don't wait anymore.
12311232
*
12321233
* It's important to do this check after the recomputation of
12331234
* RecentFlushPtr, so we can send all remaining data before shutting
@@ -1258,14 +1259,20 @@ WalSndWaitForWal(XLogRecPtr loc)
12581259
WalSndCaughtUp = true;
12591260

12601261
/*
1261-
* Try to flush pending output to the client. Also wait for the socket
1262-
* becoming writable, if there's still pending output after an attempt
1263-
* to flush. Otherwise we might just sit on output data while waiting
1264-
* for new WAL being generated.
1262+
* Try to flush any pending output to the client.
12651263
*/
12661264
if (pq_flush_if_writable() != 0)
12671265
WalSndShutdown();
12681266

1267+
/*
1268+
* If we have received CopyDone from the client, sent CopyDone
1269+
* ourselves, and the output buffer is empty, it's time to exit
1270+
* streaming, so fail the current WAL fetch request.
1271+
*/
1272+
if (streamingDoneReceiving && streamingDoneSending &&
1273+
!pq_is_send_pending())
1274+
break;
1275+
12691276
now = GetCurrentTimestamp();
12701277

12711278
/* die if timeout was reached */
@@ -1274,6 +1281,13 @@ WalSndWaitForWal(XLogRecPtr loc)
12741281
/* Send keepalive if the time has come */
12751282
WalSndKeepaliveIfNecessary(now);
12761283

1284+
/*
1285+
* Sleep until something happens or we time out. Also wait for the
1286+
* socket becoming writable, if there's still pending output.
1287+
* Otherwise we might sit on sendable output data while waiting for
1288+
* new WAL to be generated. (But if we have nothing to send, we don't
1289+
* want to wake on socket-writable.)
1290+
*/
12771291
sleeptime = WalSndComputeSleeptime(now);
12781292

12791293
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
@@ -1282,7 +1296,6 @@ WalSndWaitForWal(XLogRecPtr loc)
12821296
if (pq_is_send_pending())
12831297
wakeEvents |= WL_SOCKET_WRITEABLE;
12841298

1285-
/* Sleep until something happens or we time out */
12861299
WaitLatchOrSocket(MyLatch, wakeEvents,
12871300
MyProcPort->sock, sleeptime);
12881301
}
@@ -1870,7 +1883,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
18701883
* ourselves, and the output buffer is empty, it's time to exit
18711884
* streaming.
18721885
*/
1873-
if (!pq_is_send_pending() && streamingDoneSending && streamingDoneReceiving)
1886+
if (streamingDoneReceiving && streamingDoneSending &&
1887+
!pq_is_send_pending())
18741888
break;
18751889

18761890
/*

0 commit comments

Comments
 (0)