@@ -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 );
@@ -1191,18 +1194,16 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
1191
1194
/* Check for input from the client */
1192
1195
ProcessRepliesIfAny ();
1193
1196
1194
- now = GetCurrentTimestamp ();
1195
-
1196
1197
/* die if timeout was reached */
1197
- WalSndCheckTimeOut (now );
1198
+ WalSndCheckTimeOut ();
1198
1199
1199
1200
/* Send keepalive if the time has come */
1200
- WalSndKeepaliveIfNecessary (now );
1201
+ WalSndKeepaliveIfNecessary ();
1201
1202
1202
1203
if (!pq_is_send_pending ())
1203
1204
break ;
1204
1205
1205
- sleeptime = WalSndComputeSleeptime (now );
1206
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1206
1207
1207
1208
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1208
1209
WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1297,7 +1298,6 @@ WalSndWaitForWal(XLogRecPtr loc)
1297
1298
for (;;)
1298
1299
{
1299
1300
long sleeptime ;
1300
- TimestampTz now ;
1301
1301
1302
1302
/*
1303
1303
* Emergency bailout if postmaster has died. This is to avoid the
@@ -1382,13 +1382,11 @@ WalSndWaitForWal(XLogRecPtr loc)
1382
1382
!pq_is_send_pending ())
1383
1383
break ;
1384
1384
1385
- now = GetCurrentTimestamp ();
1386
-
1387
1385
/* die if timeout was reached */
1388
- WalSndCheckTimeOut (now );
1386
+ WalSndCheckTimeOut ();
1389
1387
1390
1388
/* Send keepalive if the time has come */
1391
- WalSndKeepaliveIfNecessary (now );
1389
+ WalSndKeepaliveIfNecessary ();
1392
1390
1393
1391
/*
1394
1392
* Sleep until something happens or we time out. Also wait for the
@@ -1397,7 +1395,7 @@ WalSndWaitForWal(XLogRecPtr loc)
1397
1395
* new WAL to be generated. (But if we have nothing to send, we don't
1398
1396
* want to wake on socket-writable.)
1399
1397
*/
1400
- sleeptime = WalSndComputeSleeptime (now );
1398
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1401
1399
1402
1400
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1403
1401
WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1594,6 +1592,8 @@ ProcessRepliesIfAny(void)
1594
1592
int r ;
1595
1593
bool received = false;
1596
1594
1595
+ last_processing = GetCurrentTimestamp ();
1596
+
1597
1597
for (;;)
1598
1598
{
1599
1599
pq_startmsgread ();
@@ -1681,7 +1681,7 @@ ProcessRepliesIfAny(void)
1681
1681
*/
1682
1682
if (received )
1683
1683
{
1684
- last_reply_timestamp = GetCurrentTimestamp () ;
1684
+ last_reply_timestamp = last_processing ;
1685
1685
waiting_for_ping_response = false;
1686
1686
}
1687
1687
}
@@ -2060,10 +2060,18 @@ WalSndComputeSleeptime(TimestampTz now)
2060
2060
2061
2061
/*
2062
2062
* Check whether there have been responses by the client within
2063
- * wal_sender_timeout and shutdown if not.
2063
+ * wal_sender_timeout and shutdown if not. Using last_processing as the
2064
+ * reference point avoids counting server-side stalls against the client.
2065
+ * However, a long server-side stall can make WalSndKeepaliveIfNecessary()
2066
+ * postdate last_processing by more than wal_sender_timeout. If that happens,
2067
+ * the client must reply almost immediately to avoid a timeout. This rarely
2068
+ * affects the default configuration, under which clients spontaneously send a
2069
+ * message every standby_message_timeout = wal_sender_timeout/6 = 10s. We
2070
+ * could eliminate that problem by recognizing timeout expiration at
2071
+ * wal_sender_timeout/2 after the keepalive.
2064
2072
*/
2065
2073
static void
2066
- WalSndCheckTimeOut (TimestampTz now )
2074
+ WalSndCheckTimeOut (void )
2067
2075
{
2068
2076
TimestampTz timeout ;
2069
2077
@@ -2074,7 +2082,7 @@ WalSndCheckTimeOut(TimestampTz now)
2074
2082
timeout = TimestampTzPlusMilliseconds (last_reply_timestamp ,
2075
2083
wal_sender_timeout );
2076
2084
2077
- if (wal_sender_timeout > 0 && now >= timeout )
2085
+ if (wal_sender_timeout > 0 && last_processing >= timeout )
2078
2086
{
2079
2087
/*
2080
2088
* Since typically expiration of replication timeout means
@@ -2105,8 +2113,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
2105
2113
*/
2106
2114
for (;;)
2107
2115
{
2108
- TimestampTz now ;
2109
-
2110
2116
/*
2111
2117
* Emergency bailout if postmaster has died. This is to avoid the
2112
2118
* necessity for manual cleanup of all postmaster children.
@@ -2184,13 +2190,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2184
2190
WalSndDone (send_data );
2185
2191
}
2186
2192
2187
- now = GetCurrentTimestamp ();
2188
-
2189
2193
/* Check for replication timeout. */
2190
- WalSndCheckTimeOut (now );
2194
+ WalSndCheckTimeOut ();
2191
2195
2192
2196
/* Send keepalive if the time has come */
2193
- WalSndKeepaliveIfNecessary (now );
2197
+ WalSndKeepaliveIfNecessary ();
2194
2198
2195
2199
/*
2196
2200
* We don't block if not caught up, unless there is unsent data
@@ -2208,7 +2212,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
2208
2212
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_TIMEOUT |
2209
2213
WL_SOCKET_READABLE ;
2210
2214
2211
- sleeptime = WalSndComputeSleeptime (now );
2215
+ /*
2216
+ * Use fresh timestamp, not last_processed, to reduce the chance
2217
+ * of reaching wal_sender_timeout before sending a keepalive.
2218
+ */
2219
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp ());
2212
2220
2213
2221
if (pq_is_send_pending ())
2214
2222
wakeEvents |= WL_SOCKET_WRITEABLE ;
@@ -3360,7 +3368,7 @@ WalSndKeepalive(bool requestReply)
3360
3368
* Send keepalive message if too much time has elapsed.
3361
3369
*/
3362
3370
static void
3363
- WalSndKeepaliveIfNecessary (TimestampTz now )
3371
+ WalSndKeepaliveIfNecessary (void )
3364
3372
{
3365
3373
TimestampTz ping_time ;
3366
3374
@@ -3381,7 +3389,7 @@ WalSndKeepaliveIfNecessary(TimestampTz now)
3381
3389
*/
3382
3390
ping_time = TimestampTzPlusMilliseconds (last_reply_timestamp ,
3383
3391
wal_sender_timeout / 2 );
3384
- if (now >= ping_time )
3392
+ if (last_processing >= ping_time )
3385
3393
{
3386
3394
WalSndKeepalive (true);
3387
3395
waiting_for_ping_response = true;
0 commit comments