@@ -152,9 +152,12 @@ static StringInfoData output_message;
152
152
static StringInfoData reply_message ;
153
153
static StringInfoData tmpbuf ;
154
154
155
+ /* Timestamp of last ProcessRepliesIfAny(). */
156
+ static TimestampTz last_processing = 0 ;
157
+
155
158
/*
156
- * Timestamp of the last receipt of the reply from the standby. Set to 0 if
157
- * wal_sender_timeout doesn't need to be active.
159
+ * Timestamp of last ProcessRepliesIfAny() that saw a reply from the
160
+ * standby. Set to 0 if wal_sender_timeout doesn't need to be active.
158
161
*/
159
162
static TimestampTz last_reply_timestamp = 0 ;
160
163
@@ -211,8 +214,8 @@ static void ProcessStandbyReplyMessage(void);
211
214
static void ProcessStandbyHSFeedbackMessage (void );
212
215
static void ProcessRepliesIfAny (void );
213
216
static void WalSndKeepalive (bool requestReply );
214
- static void WalSndKeepaliveIfNecessary (TimestampTz now );
215
- static void WalSndCheckTimeOut (TimestampTz now );
217
+ static void WalSndKeepaliveIfNecessary (void );
218
+ static void WalSndCheckTimeOut (void );
216
219
static long WalSndComputeSleeptime (TimestampTz now );
217
220
static void WalSndPrepareWrite (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
218
221
static void WalSndWriteData (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
@@ -1107,18 +1110,16 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
1107
1110
/* Check for input from the client */
1108
1111
ProcessRepliesIfAny ();
1109
1112
1110
- now = GetCurrentTimestamp ();
1111
-
1112
1113
/* die if timeout was reached */
1113
- WalSndCheckTimeOut (now );
1114
+ WalSndCheckTimeOut ();
1114
1115
1115
1116
/* Send keepalive if the time has come */
1116
- WalSndKeepaliveIfNecessary (now );
1117
+ WalSndKeepaliveIfNecessary ();
1117
1118
1118
1119
if (!pq_is_send_pending ())
1119
1120
break ;
1120
1121
1121
- sleeptime = WalSndComputeSleeptime (now );
1122
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1122
1123
1123
1124
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1124
1125
WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1186,7 +1187,6 @@ WalSndWaitForWal(XLogRecPtr loc)
1186
1187
for (;;)
1187
1188
{
1188
1189
long sleeptime ;
1189
- TimestampTz now ;
1190
1190
1191
1191
/*
1192
1192
* Emergency bailout if postmaster has died. This is to avoid the
@@ -1269,13 +1269,11 @@ WalSndWaitForWal(XLogRecPtr loc)
1269
1269
!pq_is_send_pending ())
1270
1270
break ;
1271
1271
1272
- now = GetCurrentTimestamp ();
1273
-
1274
1272
/* die if timeout was reached */
1275
- WalSndCheckTimeOut (now );
1273
+ WalSndCheckTimeOut ();
1276
1274
1277
1275
/* Send keepalive if the time has come */
1278
- WalSndKeepaliveIfNecessary (now );
1276
+ WalSndKeepaliveIfNecessary ();
1279
1277
1280
1278
/*
1281
1279
* Sleep until something happens or we time out. Also wait for the
@@ -1284,7 +1282,7 @@ WalSndWaitForWal(XLogRecPtr loc)
1284
1282
* new WAL to be generated. (But if we have nothing to send, we don't
1285
1283
* want to wake on socket-writable.)
1286
1284
*/
1287
- sleeptime = WalSndComputeSleeptime (now );
1285
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp () );
1288
1286
1289
1287
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH |
1290
1288
WL_SOCKET_READABLE | WL_TIMEOUT ;
@@ -1423,6 +1421,8 @@ ProcessRepliesIfAny(void)
1423
1421
int r ;
1424
1422
bool received = false;
1425
1423
1424
+ last_processing = GetCurrentTimestamp ();
1425
+
1426
1426
for (;;)
1427
1427
{
1428
1428
pq_startmsgread ();
@@ -1510,7 +1510,7 @@ ProcessRepliesIfAny(void)
1510
1510
*/
1511
1511
if (received )
1512
1512
{
1513
- last_reply_timestamp = GetCurrentTimestamp () ;
1513
+ last_reply_timestamp = last_processing ;
1514
1514
waiting_for_ping_response = false;
1515
1515
}
1516
1516
}
@@ -1806,10 +1806,18 @@ WalSndComputeSleeptime(TimestampTz now)
1806
1806
1807
1807
/*
1808
1808
* Check whether there have been responses by the client within
1809
- * wal_sender_timeout and shutdown if not.
1809
+ * wal_sender_timeout and shutdown if not. Using last_processing as the
1810
+ * reference point avoids counting server-side stalls against the client.
1811
+ * However, a long server-side stall can make WalSndKeepaliveIfNecessary()
1812
+ * postdate last_processing by more than wal_sender_timeout. If that happens,
1813
+ * the client must reply almost immediately to avoid a timeout. This rarely
1814
+ * affects the default configuration, under which clients spontaneously send a
1815
+ * message every standby_message_timeout = wal_sender_timeout/6 = 10s. We
1816
+ * could eliminate that problem by recognizing timeout expiration at
1817
+ * wal_sender_timeout/2 after the keepalive.
1810
1818
*/
1811
1819
static void
1812
- WalSndCheckTimeOut (TimestampTz now )
1820
+ WalSndCheckTimeOut (void )
1813
1821
{
1814
1822
TimestampTz timeout ;
1815
1823
@@ -1820,7 +1828,7 @@ WalSndCheckTimeOut(TimestampTz now)
1820
1828
timeout = TimestampTzPlusMilliseconds (last_reply_timestamp ,
1821
1829
wal_sender_timeout );
1822
1830
1823
- if (wal_sender_timeout > 0 && now >= timeout )
1831
+ if (wal_sender_timeout > 0 && last_processing >= timeout )
1824
1832
{
1825
1833
/*
1826
1834
* Since typically expiration of replication timeout means
@@ -1851,8 +1859,6 @@ WalSndLoop(WalSndSendDataCallback send_data)
1851
1859
*/
1852
1860
for (;;)
1853
1861
{
1854
- TimestampTz now ;
1855
-
1856
1862
/*
1857
1863
* Emergency bailout if postmaster has died. This is to avoid the
1858
1864
* necessity for manual cleanup of all postmaster children.
@@ -1930,13 +1936,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
1930
1936
WalSndDone (send_data );
1931
1937
}
1932
1938
1933
- now = GetCurrentTimestamp ();
1934
-
1935
1939
/* Check for replication timeout. */
1936
- WalSndCheckTimeOut (now );
1940
+ WalSndCheckTimeOut ();
1937
1941
1938
1942
/* Send keepalive if the time has come */
1939
- WalSndKeepaliveIfNecessary (now );
1943
+ WalSndKeepaliveIfNecessary ();
1940
1944
1941
1945
/*
1942
1946
* We don't block if not caught up, unless there is unsent data
@@ -1954,7 +1958,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
1954
1958
wakeEvents = WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_TIMEOUT |
1955
1959
WL_SOCKET_READABLE ;
1956
1960
1957
- sleeptime = WalSndComputeSleeptime (now );
1961
+ /*
1962
+ * Use fresh timestamp, not last_processed, to reduce the chance
1963
+ * of reaching wal_sender_timeout before sending a keepalive.
1964
+ */
1965
+ sleeptime = WalSndComputeSleeptime (GetCurrentTimestamp ());
1958
1966
1959
1967
if (pq_is_send_pending ())
1960
1968
wakeEvents |= WL_SOCKET_WRITEABLE ;
@@ -3051,7 +3059,7 @@ WalSndKeepalive(bool requestReply)
3051
3059
* Send keepalive message if too much time has elapsed.
3052
3060
*/
3053
3061
static void
3054
- WalSndKeepaliveIfNecessary (TimestampTz now )
3062
+ WalSndKeepaliveIfNecessary (void )
3055
3063
{
3056
3064
TimestampTz ping_time ;
3057
3065
@@ -3072,7 +3080,7 @@ WalSndKeepaliveIfNecessary(TimestampTz now)
3072
3080
*/
3073
3081
ping_time = TimestampTzPlusMilliseconds (last_reply_timestamp ,
3074
3082
wal_sender_timeout / 2 );
3075
- if (now >= ping_time )
3083
+ if (last_processing >= ping_time )
3076
3084
{
3077
3085
WalSndKeepalive (true);
3078
3086
waiting_for_ping_response = true;
0 commit comments