@@ -162,9 +162,12 @@ static StringInfoData output_message;
162
162
static StringInfoData reply_message ;
163
163
static StringInfoData tmpbuf ;
164
164
165
+ /* Timestamp of last ProcessRepliesIfAny(). */
166
+ static TimestampTz last_processing = 0 ;
167
+
165
168
/*
166
- * Timestamp of the last receipt of the reply from the standby. Set to 0 if
167
- * wal_sender_timeout doesn't need to be active.
169
+ * Timestamp of last ProcessRepliesIfAny() that saw a reply from the
170
+ * standby. Set to 0 if wal_sender_timeout doesn't need to be active.
168
171
*/
169
172
static TimestampTz last_reply_timestamp = 0 ;
170
173
@@ -241,8 +244,8 @@ static void ProcessStandbyReplyMessage(void);
241
244
static void ProcessStandbyHSFeedbackMessage (void );
242
245
static void ProcessRepliesIfAny (void );
243
246
static void WalSndKeepalive (bool requestReply );
244
- static void WalSndKeepaliveIfNecessary (TimestampTz now );
245
- static void WalSndCheckTimeOut (TimestampTz now );
247
+ static void WalSndKeepaliveIfNecessary (void );
248
+ static void WalSndCheckTimeOut (void );
246
249
static long WalSndComputeSleeptime (TimestampTz now );
247
250
static void WalSndPrepareWrite (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
248
251
static void WalSndWriteData (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
@@ -1195,18 +1198,16 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
1195
1198
/* Check for input from the client */
1196
1199
ProcessRepliesIfAny ();
1197
1200
1198
- now = GetCurrentTimestamp ();
1199
-
1200
1201
/* die if timeout was reached */
1201
- WalSndCheckTimeOut (now );
1202
+ WalSndCheckTimeOut ();
1202
1203
1203
1204
/* Send keepalive if the time has come */
1204
- WalSndKeepaliveIfNecessary (now );
1205
+ WalSndKeepaliveIfNecessary ();
1205
1206
1206
1207
if (!pq_is_send_pending ())
1207
1208
break ;
1208
1209
1209
- sleeptime = WalSndComputeSleeptime (now );
1210
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1210
1211
1211
1212
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1212
1213
WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1301,7 +1302,6 @@ WalSndWaitForWal(XLogRecPtr loc)
1301
1302
for (;;)
1302
1303
{
1303
1304
long sleeptime ;
1304
- TimestampTz now ;
1305
1305
1306
1306
/*
1307
1307
* Emergency bailout if postmaster has died. This is to avoid the
@@ -1386,13 +1386,11 @@ WalSndWaitForWal(XLogRecPtr loc)
1386
1386
!pq_is_send_pending ())
1387
1387
break ;
1388
1388
1389
- now = GetCurrentTimestamp ();
1390
-
1391
1389
/* die if timeout was reached */
1392
- WalSndCheckTimeOut (now );
1390
+ WalSndCheckTimeOut ();
1393
1391
1394
1392
/* Send keepalive if the time has come */
1395
- WalSndKeepaliveIfNecessary (now );
1393
+ WalSndKeepaliveIfNecessary ();
1396
1394
1397
1395
/*
1398
1396
* Sleep until something happens or we time out. Also wait for the
@@ -1401,7 +1399,7 @@ WalSndWaitForWal(XLogRecPtr loc)
1401
1399
* new WAL to be generated. (But if we have nothing to send, we don't
1402
1400
* want to wake on socket-writable.)
1403
1401
*/
1404
- sleeptime = WalSndComputeSleeptime (now );
1402
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1405
1403
1406
1404
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1407
1405
WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1598,6 +1596,8 @@ ProcessRepliesIfAny(void)
1598
1596
int r ;
1599
1597
bool received = false;
1600
1598
1599
+ last_processing = GetCurrentTimestamp ();
1600
+
1601
1601
for (;;)
1602
1602
{
1603
1603
pq_startmsgread ();
@@ -1685,7 +1685,7 @@ ProcessRepliesIfAny(void)
1685
1685
*/
1686
1686
if (received )
1687
1687
{
1688
- last_reply_timestamp = GetCurrentTimestamp () ;
1688
+ last_reply_timestamp = last_processing ;
1689
1689
waiting_for_ping_response = false;
1690
1690
}
1691
1691
}
@@ -2064,10 +2064,18 @@ WalSndComputeSleeptime(TimestampTz now)
2064
2064
2065
2065
/*
2066
2066
* Check whether there have been responses by the client within
2067
- * wal_sender_timeout and shutdown if not.
2067
+ * wal_sender_timeout and shutdown if not. Using last_processing as the
2068
+ * reference point avoids counting server-side stalls against the client.
2069
+ * However, a long server-side stall can make WalSndKeepaliveIfNecessary()
2070
+ * postdate last_processing by more than wal_sender_timeout. If that happens,
2071
+ * the client must reply almost immediately to avoid a timeout. This rarely
2072
+ * affects the default configuration, under which clients spontaneously send a
2073
+ * message every standby_message_timeout = wal_sender_timeout/6 = 10s. We
2074
+ * could eliminate that problem by recognizing timeout expiration at
2075
+ * wal_sender_timeout/2 after the keepalive.
2068
2076
*/
2069
2077
static void
2070
- WalSndCheckTimeOut (TimestampTz now )
2078
+ WalSndCheckTimeOut (void )
2071
2079
{
2072
2080
TimestampTz timeout ;
2073
2081
@@ -2078,7 +2086,7 @@ WalSndCheckTimeOut(TimestampTz now)
2078
2086
timeout = TimestampTzPlusMilliseconds (last_reply_timestamp ,
2079
2087
wal_sender_timeout );
2080
2088
2081
- if (wal_sender_timeout > 0 && now >= timeout )
2089
+ if (wal_sender_timeout > 0 && last_processing >= timeout )
2082
2090
{
2083
2091
/*
2084
2092
* Since typically expiration of replication timeout means
@@ -2109,8 +2117,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
2109
2117
*/
2110
2118
for (;;)
2111
2119
{
2112
- TimestampTz now ;
2113
-
2114
2120
/*
2115
2121
* Emergency bailout if postmaster has died. This is to avoid the
2116
2122
* necessity for manual cleanup of all postmaster children.
@@ -2188,13 +2194,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2188
2194
WalSndDone (send_data );
2189
2195
}
2190
2196
2191
- now = GetCurrentTimestamp ();
2192
-
2193
2197
/* Check for replication timeout. */
2194
- WalSndCheckTimeOut (now );
2198
+ WalSndCheckTimeOut ();
2195
2199
2196
2200
/* Send keepalive if the time has come */
2197
- WalSndKeepaliveIfNecessary (now );
2201
+ WalSndKeepaliveIfNecessary ();
2198
2202
2199
2203
/*
2200
2204
* We don't block if not caught up, unless there is unsent data
@@ -2212,7 +2216,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2212
2216
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_TIMEOUT |
2213
2217
WL_SOCKET_READABLE ;
2214
2218
2215
- sleeptime = WalSndComputeSleeptime (now );
2219
+ /*
2220
+ * Use fresh timestamp, not last_processed, to reduce the chance
2221
+ * of reaching wal_sender_timeout before sending a keepalive.
2222
+ */
2223
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp ());
2216
2224
2217
2225
if (pq_is_send_pending ())
2218
2226
wakeEvents |= WL_SOCKET_WRITEABLE ;
@@ -3364,7 +3372,7 @@ WalSndKeepalive(bool requestReply)
3364
3372
* Send keepalive message if too much time has elapsed.
3365
3373
*/
3366
3374
static void
3367
- WalSndKeepaliveIfNecessary (TimestampTz now )
3375
+ WalSndKeepaliveIfNecessary (void )
3368
3376
{
3369
3377
TimestampTz ping_time ;
3370
3378
@@ -3385,7 +3393,7 @@ WalSndKeepaliveIfNecessary(TimestampTz now)
3385
3393
*/
3386
3394
ping_time = TimestampTzPlusMilliseconds (last_reply_timestamp ,
3387
3395
wal_sender_timeout / 2 );
3388
- if (now >= ping_time )
3396
+ if (last_processing >= ping_time )
3389
3397
{
3390
3398
WalSndKeepalive (true);
3391
3399
waiting_for_ping_response = true;
0 commit comments