@@ -242,6 +242,7 @@ static void WalSndPrepareWrite(LogicalDecodingContext *ctx, XLogRecPtr lsn, Tran
242
242
static void WalSndWriteData (LogicalDecodingContext * ctx , XLogRecPtr lsn , TransactionId xid , bool last_write );
243
243
static XLogRecPtr WalSndWaitForWal (XLogRecPtr loc );
244
244
static TimeOffset LagTrackerRead (int head , XLogRecPtr lsn , TimestampTz now );
245
+ static bool TransactionIdInRecentPast (TransactionId xid , uint32 epoch );
245
246
246
247
static void XLogRead (char * buf , XLogRecPtr startptr , Size count );
247
248
@@ -1756,7 +1757,7 @@ ProcessStandbyReplyMessage(void)
1756
1757
1757
1758
/* compute new replication slot xmin horizon if needed */
1758
1759
static void
1759
- PhysicalReplicationSlotNewXmin (TransactionId feedbackXmin )
1760
+ PhysicalReplicationSlotNewXmin (TransactionId feedbackXmin , TransactionId feedbackCatalogXmin )
1760
1761
{
1761
1762
bool changed = false;
1762
1763
ReplicationSlot * slot = MyReplicationSlot ;
@@ -1777,6 +1778,14 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin)
1777
1778
slot -> data .xmin = feedbackXmin ;
1778
1779
slot -> effective_xmin = feedbackXmin ;
1779
1780
}
1781
+ if (!TransactionIdIsNormal (slot -> data .catalog_xmin ) ||
1782
+ !TransactionIdIsNormal (feedbackCatalogXmin ) ||
1783
+ TransactionIdPrecedes (slot -> data .catalog_xmin , feedbackCatalogXmin ))
1784
+ {
1785
+ changed = true;
1786
+ slot -> data .catalog_xmin = feedbackCatalogXmin ;
1787
+ slot -> effective_catalog_xmin = feedbackCatalogXmin ;
1788
+ }
1780
1789
SpinLockRelease (& slot -> mutex );
1781
1790
1782
1791
if (changed )
@@ -1786,60 +1795,93 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin)
1786
1795
}
1787
1796
}
1788
1797
1798
+ /*
1799
+ * Check that the provided xmin/epoch are sane, that is, not in the future
1800
+ * and not so far back as to be already wrapped around.
1801
+ *
1802
+ * Epoch of nextXid should be same as standby, or if the counter has
1803
+ * wrapped, then one greater than standby.
1804
+ *
1805
+ * This check doesn't care about whether clog exists for these xids
1806
+ * at all.
1807
+ */
1808
+ static bool
1809
+ TransactionIdInRecentPast (TransactionId xid , uint32 epoch )
1810
+ {
1811
+ TransactionId nextXid ;
1812
+ uint32 nextEpoch ;
1813
+
1814
+ GetNextXidAndEpoch (& nextXid , & nextEpoch );
1815
+
1816
+ if (xid <= nextXid )
1817
+ {
1818
+ if (epoch != nextEpoch )
1819
+ return false;
1820
+ }
1821
+ else
1822
+ {
1823
+ if (epoch + 1 != nextEpoch )
1824
+ return false;
1825
+ }
1826
+
1827
+ if (!TransactionIdPrecedesOrEquals (xid , nextXid ))
1828
+ return false; /* epoch OK, but it's wrapped around */
1829
+
1830
+ return true;
1831
+ }
1832
+
1789
1833
/*
1790
1834
* Hot Standby feedback
1791
1835
*/
1792
1836
static void
1793
1837
ProcessStandbyHSFeedbackMessage (void )
1794
1838
{
1795
- TransactionId nextXid ;
1796
- uint32 nextEpoch ;
1797
1839
TransactionId feedbackXmin ;
1798
1840
uint32 feedbackEpoch ;
1841
+ TransactionId feedbackCatalogXmin ;
1842
+ uint32 feedbackCatalogEpoch ;
1799
1843
1800
1844
/*
1801
1845
* Decipher the reply message. The caller already consumed the msgtype
1802
- * byte.
1846
+ * byte. See XLogWalRcvSendHSFeedback() in walreceiver.c for the creation
1847
+ * of this message.
1803
1848
*/
1804
1849
(void ) pq_getmsgint64 (& reply_message ); /* sendTime; not used ATM */
1805
1850
feedbackXmin = pq_getmsgint (& reply_message , 4 );
1806
1851
feedbackEpoch = pq_getmsgint (& reply_message , 4 );
1852
+ feedbackCatalogXmin = pq_getmsgint (& reply_message , 4 );
1853
+ feedbackCatalogEpoch = pq_getmsgint (& reply_message , 4 );
1807
1854
1808
- elog (DEBUG2 , "hot standby feedback xmin %u epoch %u" ,
1855
+ elog (DEBUG2 , "hot standby feedback xmin %u epoch %u, catalog_xmin %u epoch %u " ,
1809
1856
feedbackXmin ,
1810
- feedbackEpoch );
1857
+ feedbackEpoch ,
1858
+ feedbackCatalogXmin ,
1859
+ feedbackCatalogEpoch );
1811
1860
1812
- /* Unset WalSender's xmin if the feedback message value is invalid */
1813
- if (!TransactionIdIsNormal (feedbackXmin ))
1861
+ /*
1862
+ * Unset WalSender's xmins if the feedback message values are invalid.
1863
+ * This happens when the downstream turned hot_standby_feedback off.
1864
+ */
1865
+ if (!TransactionIdIsNormal (feedbackXmin )
1866
+ && !TransactionIdIsNormal (feedbackCatalogXmin ))
1814
1867
{
1815
1868
MyPgXact -> xmin = InvalidTransactionId ;
1816
1869
if (MyReplicationSlot != NULL )
1817
- PhysicalReplicationSlotNewXmin (feedbackXmin );
1870
+ PhysicalReplicationSlotNewXmin (feedbackXmin , feedbackCatalogXmin );
1818
1871
return ;
1819
1872
}
1820
1873
1821
1874
/*
1822
1875
* Check that the provided xmin/epoch are sane, that is, not in the future
1823
1876
* and not so far back as to be already wrapped around. Ignore if not.
1824
- *
1825
- * Epoch of nextXid should be same as standby, or if the counter has
1826
- * wrapped, then one greater than standby.
1827
1877
*/
1828
- GetNextXidAndEpoch (& nextXid , & nextEpoch );
1829
-
1830
- if (feedbackXmin <= nextXid )
1831
- {
1832
- if (feedbackEpoch != nextEpoch )
1833
- return ;
1834
- }
1835
- else
1836
- {
1837
- if (feedbackEpoch + 1 != nextEpoch )
1838
- return ;
1839
- }
1878
+ if (TransactionIdIsNormal (feedbackXmin ) &&
1879
+ !TransactionIdInRecentPast (feedbackXmin , feedbackEpoch ))
1880
+ return ;
1840
1881
1841
- if (!TransactionIdPrecedesOrEquals (feedbackXmin , nextXid ))
1842
- return ; /* epoch OK, but it's wrapped around */
1882
+ if (TransactionIdIsNormal (feedbackCatalogXmin ) &&
1883
+ !TransactionIdInRecentPast (feedbackCatalogXmin , feedbackCatalogEpoch ))
1884
+ return ;
1843
1885
1844
1886
/*
1845
1887
* Set the WalSender's xmin equal to the standby's requested xmin, so that
@@ -1864,15 +1906,23 @@ ProcessStandbyHSFeedbackMessage(void)
1864
1906
* already since a VACUUM could have just finished calling GetOldestXmin.)
1865
1907
*
1866
1908
* If we're using a replication slot we reserve the xmin via that,
1867
- * otherwise via the walsender's PGXACT entry.
1909
+ * otherwise via the walsender's PGXACT entry. We can only track the
1910
+ * catalog xmin separately when using a slot, so we store the least
1911
+ * of the two provided when not using a slot.
1868
1912
*
1869
1913
* XXX: It might make sense to generalize the ephemeral slot concept and
1870
1914
* always use the slot mechanism to handle the feedback xmin.
1871
1915
*/
1872
1916
if (MyReplicationSlot != NULL ) /* XXX: persistency configurable? */
1873
- PhysicalReplicationSlotNewXmin (feedbackXmin );
1917
+ PhysicalReplicationSlotNewXmin (feedbackXmin , feedbackCatalogXmin );
1874
1918
else
1875
- MyPgXact -> xmin = feedbackXmin ;
1919
+ {
1920
+ if (TransactionIdIsNormal (feedbackCatalogXmin )
1921
+ && TransactionIdPrecedes (feedbackCatalogXmin , feedbackXmin ))
1922
+ MyPgXact -> xmin = feedbackCatalogXmin ;
1923
+ else
1924
+ MyPgXact -> xmin = feedbackXmin ;
1925
+ }
1876
1926
}
1877
1927
1878
1928
/*
0 commit comments