@@ -1763,21 +1763,33 @@ ProcessStandbyReplyMessage(void)
17631763 applyLag ;
17641764 bool clearLagTimes ;
17651765 TimestampTz now ;
1766+ TimestampTz replyTime ;
17661767
17671768 static bool fullyAppliedLastTime = false;
17681769
17691770 /* the caller already consumed the msgtype byte */
17701771 writePtr = pq_getmsgint64 (& reply_message );
17711772 flushPtr = pq_getmsgint64 (& reply_message );
17721773 applyPtr = pq_getmsgint64 (& reply_message );
1773- ( void ) pq_getmsgint64 (& reply_message ); /* sendTime; not used ATM */
1774+ replyTime = pq_getmsgint64 (& reply_message );
17741775 replyRequested = pq_getmsgbyte (& reply_message );
17751776
1776- elog (DEBUG2 , "write %X/%X flush %X/%X apply %X/%X%s" ,
1777- (uint32 ) (writePtr >> 32 ), (uint32 ) writePtr ,
1778- (uint32 ) (flushPtr >> 32 ), (uint32 ) flushPtr ,
1779- (uint32 ) (applyPtr >> 32 ), (uint32 ) applyPtr ,
1780- replyRequested ? " (reply requested)" : "" );
1777+ if (log_min_messages <= DEBUG2 )
1778+ {
1779+ char * replyTimeStr ;
1780+
1781+ /* Copy because timestamptz_to_str returns a static buffer */
1782+ replyTimeStr = pstrdup (timestamptz_to_str (replyTime ));
1783+
1784+ elog (DEBUG2 , "write %X/%X flush %X/%X apply %X/%X%s reply_time %s" ,
1785+ (uint32 ) (writePtr >> 32 ), (uint32 ) writePtr ,
1786+ (uint32 ) (flushPtr >> 32 ), (uint32 ) flushPtr ,
1787+ (uint32 ) (applyPtr >> 32 ), (uint32 ) applyPtr ,
1788+ replyRequested ? " (reply requested)" : "" ,
1789+ replyTimeStr );
1790+
1791+ pfree (replyTimeStr );
1792+ }
17811793
17821794 /* See if we can compute the round-trip lag for these positions. */
17831795 now = GetCurrentTimestamp ();
@@ -1824,6 +1836,7 @@ ProcessStandbyReplyMessage(void)
18241836 walsnd -> flushLag = flushLag ;
18251837 if (applyLag != -1 || clearLagTimes )
18261838 walsnd -> applyLag = applyLag ;
1839+ walsnd -> replyTime = replyTime ;
18271840 SpinLockRelease (& walsnd -> mutex );
18281841 }
18291842
@@ -1927,23 +1940,47 @@ ProcessStandbyHSFeedbackMessage(void)
19271940 uint32 feedbackEpoch ;
19281941 TransactionId feedbackCatalogXmin ;
19291942 uint32 feedbackCatalogEpoch ;
1943+ TimestampTz replyTime ;
19301944
19311945 /*
19321946 * Decipher the reply message. The caller already consumed the msgtype
19331947 * byte. See XLogWalRcvSendHSFeedback() in walreceiver.c for the creation
19341948 * of this message.
19351949 */
1936- ( void ) pq_getmsgint64 (& reply_message ); /* sendTime; not used ATM */
1950+ replyTime = pq_getmsgint64 (& reply_message );
19371951 feedbackXmin = pq_getmsgint (& reply_message , 4 );
19381952 feedbackEpoch = pq_getmsgint (& reply_message , 4 );
19391953 feedbackCatalogXmin = pq_getmsgint (& reply_message , 4 );
19401954 feedbackCatalogEpoch = pq_getmsgint (& reply_message , 4 );
19411955
1942- elog (DEBUG2 , "hot standby feedback xmin %u epoch %u, catalog_xmin %u epoch %u" ,
1943- feedbackXmin ,
1944- feedbackEpoch ,
1945- feedbackCatalogXmin ,
1946- feedbackCatalogEpoch );
1956+ if (log_min_messages <= DEBUG2 )
1957+ {
1958+ char * replyTimeStr ;
1959+
1960+ /* Copy because timestamptz_to_str returns a static buffer */
1961+ replyTimeStr = pstrdup (timestamptz_to_str (replyTime ));
1962+
1963+ elog (DEBUG2 , "hot standby feedback xmin %u epoch %u, catalog_xmin %u epoch %u reply_time %s" ,
1964+ feedbackXmin ,
1965+ feedbackEpoch ,
1966+ feedbackCatalogXmin ,
1967+ feedbackCatalogEpoch ,
1968+ replyTimeStr );
1969+
1970+ pfree (replyTimeStr );
1971+ }
1972+
1973+ /*
1974+ * Update shared state for this WalSender process based on reply data from
1975+ * standby.
1976+ */
1977+ {
1978+ WalSnd * walsnd = MyWalSnd ;
1979+
1980+ SpinLockAcquire (& walsnd -> mutex );
1981+ walsnd -> replyTime = replyTime ;
1982+ SpinLockRelease (& walsnd -> mutex );
1983+ }
19471984
19481985 /*
19491986 * Unset WalSender's xmins if the feedback message values are invalid.
@@ -2265,6 +2302,7 @@ InitWalSenderSlot(void)
22652302 walsnd -> applyLag = -1 ;
22662303 walsnd -> state = WALSNDSTATE_STARTUP ;
22672304 walsnd -> latch = & MyProc -> procLatch ;
2305+ walsnd -> replyTime = 0 ;
22682306 SpinLockRelease (& walsnd -> mutex );
22692307 /* don't need the lock anymore */
22702308 MyWalSnd = (WalSnd * ) walsnd ;
@@ -3179,7 +3217,7 @@ offset_to_interval(TimeOffset offset)
31793217Datum
31803218pg_stat_get_wal_senders (PG_FUNCTION_ARGS )
31813219{
3182- #define PG_STAT_GET_WAL_SENDERS_COLS 11
3220+ #define PG_STAT_GET_WAL_SENDERS_COLS 12
31833221 ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
31843222 TupleDesc tupdesc ;
31853223 Tuplestorestate * tupstore ;
@@ -3233,6 +3271,7 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
32333271 int priority ;
32343272 int pid ;
32353273 WalSndState state ;
3274+ TimestampTz replyTime ;
32363275 Datum values [PG_STAT_GET_WAL_SENDERS_COLS ];
32373276 bool nulls [PG_STAT_GET_WAL_SENDERS_COLS ];
32383277
@@ -3252,6 +3291,7 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
32523291 flushLag = walsnd -> flushLag ;
32533292 applyLag = walsnd -> applyLag ;
32543293 priority = walsnd -> sync_standby_priority ;
3294+ replyTime = walsnd -> replyTime ;
32553295 SpinLockRelease (& walsnd -> mutex );
32563296
32573297 memset (nulls , 0 , sizeof (nulls ));
@@ -3328,6 +3368,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
33283368 CStringGetTextDatum ("sync" ) : CStringGetTextDatum ("quorum" );
33293369 else
33303370 values [10 ] = CStringGetTextDatum ("potential" );
3371+
3372+ if (replyTime == 0 )
3373+ nulls [11 ] = true;
3374+ else
3375+ values [11 ] = TimestampTzGetDatum (replyTime );
33313376 }
33323377
33333378 tuplestore_putvalues (tupstore , tupdesc , values , nulls );
0 commit comments