@@ -161,9 +161,12 @@ static StringInfoData output_message;
161
161
static StringInfoData reply_message ;
162
162
static StringInfoData tmpbuf ;
163
163
164
+ /* Timestamp of last ProcessRepliesIfAny(). */
165
+ static TimestampTz last_processing = 0 ;
166
+
164
167
/*
165
- * Timestamp of the last receipt of the reply from the standby. Set to 0 if
166
- * wal_sender_timeout doesn't need to be active.
168
+ * Timestamp of last ProcessRepliesIfAny() that saw a reply from the
169
+ * standby. Set to 0 if wal_sender_timeout doesn't need to be active.
167
170
*/
168
171
static TimestampTz last_reply_timestamp = 0 ;
169
172
@@ -240,8 +243,8 @@ static void ProcessStandbyReplyMessage(void);
240
243
static void ProcessStandbyHSFeedbackMessage (void );
241
244
static void ProcessRepliesIfAny (void );
242
245
static void WalSndKeepalive (bool requestReply );
243
- static void WalSndKeepaliveIfNecessary (TimestampTz now );
244
- static void WalSndCheckTimeOut (TimestampTz now );
246
+ static void WalSndKeepaliveIfNecessary (void );
247
+ static void WalSndCheckTimeOut (void );
245
248
static long WalSndComputeSleeptime (TimestampTz now );
246
249
static void WalSndPrepareWrite (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
247
250
static void WalSndWriteData (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
@@ -1202,18 +1205,16 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
1202
1205
/* Check for input from the client */
1203
1206
ProcessRepliesIfAny ();
1204
1207
1205
- now = GetCurrentTimestamp ();
1206
-
1207
1208
/* die if timeout was reached */
1208
- WalSndCheckTimeOut (now );
1209
+ WalSndCheckTimeOut ();
1209
1210
1210
1211
/* Send keepalive if the time has come */
1211
- WalSndKeepaliveIfNecessary (now );
1212
+ WalSndKeepaliveIfNecessary ();
1212
1213
1213
1214
if (!pq_is_send_pending ())
1214
1215
break ;
1215
1216
1216
- sleeptime = WalSndComputeSleeptime (now );
1217
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1217
1218
1218
1219
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1219
1220
WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1308,7 +1309,6 @@ WalSndWaitForWal(XLogRecPtr loc)
1308
1309
for (;;)
1309
1310
{
1310
1311
long sleeptime ;
1311
- TimestampTz now ;
1312
1312
1313
1313
/*
1314
1314
* Emergency bailout if postmaster has died. This is to avoid the
@@ -1393,13 +1393,11 @@ WalSndWaitForWal(XLogRecPtr loc)
1393
1393
!pq_is_send_pending ())
1394
1394
break ;
1395
1395
1396
- now = GetCurrentTimestamp ();
1397
-
1398
1396
/* die if timeout was reached */
1399
- WalSndCheckTimeOut (now );
1397
+ WalSndCheckTimeOut ();
1400
1398
1401
1399
/* Send keepalive if the time has come */
1402
- WalSndKeepaliveIfNecessary (now );
1400
+ WalSndKeepaliveIfNecessary ();
1403
1401
1404
1402
/*
1405
1403
* Sleep until something happens or we time out. Also wait for the
@@ -1408,7 +1406,7 @@ WalSndWaitForWal(XLogRecPtr loc)
1408
1406
* new WAL to be generated. (But if we have nothing to send, we don't
1409
1407
* want to wake on socket-writable.)
1410
1408
*/
1411
- sleeptime = WalSndComputeSleeptime (now );
1409
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1412
1410
1413
1411
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1414
1412
WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1605,6 +1603,8 @@ ProcessRepliesIfAny(void)
1605
1603
int r ;
1606
1604
bool received = false;
1607
1605
1606
+ last_processing = GetCurrentTimestamp ();
1607
+
1608
1608
for (;;)
1609
1609
{
1610
1610
pq_startmsgread ();
@@ -1692,7 +1692,7 @@ ProcessRepliesIfAny(void)
1692
1692
*/
1693
1693
if (received )
1694
1694
{
1695
- last_reply_timestamp = GetCurrentTimestamp () ;
1695
+ last_reply_timestamp = last_processing ;
1696
1696
waiting_for_ping_response = false;
1697
1697
}
1698
1698
}
@@ -2071,10 +2071,18 @@ WalSndComputeSleeptime(TimestampTz now)
2071
2071
2072
2072
/*
2073
2073
* Check whether there have been responses by the client within
2074
- * wal_sender_timeout and shutdown if not.
2074
+ * wal_sender_timeout and shutdown if not. Using last_processing as the
2075
+ * reference point avoids counting server-side stalls against the client.
2076
+ * However, a long server-side stall can make WalSndKeepaliveIfNecessary()
2077
+ * postdate last_processing by more than wal_sender_timeout. If that happens,
2078
+ * the client must reply almost immediately to avoid a timeout. This rarely
2079
+ * affects the default configuration, under which clients spontaneously send a
2080
+ * message every standby_message_timeout = wal_sender_timeout/6 = 10s. We
2081
+ * could eliminate that problem by recognizing timeout expiration at
2082
+ * wal_sender_timeout/2 after the keepalive.
2075
2083
*/
2076
2084
static void
2077
- WalSndCheckTimeOut (TimestampTz now )
2085
+ WalSndCheckTimeOut (void )
2078
2086
{
2079
2087
TimestampTz timeout ;
2080
2088
@@ -2085,7 +2093,7 @@ WalSndCheckTimeOut(TimestampTz now)
2085
2093
timeout = TimestampTzPlusMilliseconds (last_reply_timestamp ,
2086
2094
wal_sender_timeout );
2087
2095
2088
- if (wal_sender_timeout > 0 && now >= timeout )
2096
+ if (wal_sender_timeout > 0 && last_processing >= timeout )
2089
2097
{
2090
2098
/*
2091
2099
* Since typically expiration of replication timeout means
@@ -2116,8 +2124,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
2116
2124
*/
2117
2125
for (;;)
2118
2126
{
2119
- TimestampTz now ;
2120
-
2121
2127
/*
2122
2128
* Emergency bailout if postmaster has died. This is to avoid the
2123
2129
* necessity for manual cleanup of all postmaster children.
@@ -2195,13 +2201,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2195
2201
WalSndDone (send_data );
2196
2202
}
2197
2203
2198
- now = GetCurrentTimestamp ();
2199
-
2200
2204
/* Check for replication timeout. */
2201
- WalSndCheckTimeOut (now );
2205
+ WalSndCheckTimeOut ();
2202
2206
2203
2207
/* Send keepalive if the time has come */
2204
- WalSndKeepaliveIfNecessary (now );
2208
+ WalSndKeepaliveIfNecessary ();
2205
2209
2206
2210
/*
2207
2211
* We don't block if not caught up, unless there is unsent data
@@ -2219,7 +2223,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2219
2223
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_TIMEOUT |
2220
2224
WL_SOCKET_READABLE ;
2221
2225
2222
- sleeptime = WalSndComputeSleeptime (now );
2226
+ /*
2227
+ * Use fresh timestamp, not last_processed, to reduce the chance
2228
+ * of reaching wal_sender_timeout before sending a keepalive.
2229
+ */
2230
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp ());
2223
2231
2224
2232
if (pq_is_send_pending ())
2225
2233
wakeEvents |= WL_SOCKET_WRITEABLE ;
@@ -3379,7 +3387,7 @@ WalSndKeepalive(bool requestReply)
3379
3387
* Send keepalive message if too much time has elapsed.
3380
3388
*/
3381
3389
static void
3382
- WalSndKeepaliveIfNecessary (TimestampTz now )
3390
+ WalSndKeepaliveIfNecessary (void )
3383
3391
{
3384
3392
TimestampTz ping_time ;
3385
3393
@@ -3400,7 +3408,7 @@ WalSndKeepaliveIfNecessary(TimestampTz now)
3400
3408
*/
3401
3409
ping_time = TimestampTzPlusMilliseconds (last_reply_timestamp ,
3402
3410
wal_sender_timeout / 2 );
3403
- if (now >= ping_time )
3411
+ if (last_processing >= ping_time )
3404
3412
{
3405
3413
WalSndKeepalive (true);
3406
3414
waiting_for_ping_response = true;
0 commit comments