Skip to content

Commit 5737c12

Browse files
Report catalog_xmin separately in hot_standby_feedback
If the upstream walsender is using a physical replication slot, store the catalog_xmin in the slot's catalog_xmin field. If the upstream doesn't use a slot and has only a PGPROC entry behaviour doesn't change, as we store the combined xmin and catalog_xmin in the PGPROC entry. Author: Craig Ringer
1 parent 4dd3abe commit 5737c12

File tree

7 files changed

+199
-51
lines changed

7 files changed

+199
-51
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,10 +1916,11 @@ The commands accepted in walsender mode are:
19161916
</term>
19171917
<listitem>
19181918
<para>
1919-
The standby's current xmin. This may be 0, if the standby is
1920-
sending notification that Hot Standby feedback will no longer
1921-
be sent on this connection. Later non-zero messages may
1922-
reinitiate the feedback mechanism.
1919+
The standby's current global xmin, excluding the catalog_xmin from any
1920+
replication slots. If both this value and the following
1921+
catalog_xmin are 0 this is treated as a notification that Hot Standby
1922+
feedback will no longer be sent on this connection. Later non-zero
1923+
messages may reinitiate the feedback mechanism.
19231924
</para>
19241925
</listitem>
19251926
</varlistentry>
@@ -1929,7 +1930,29 @@ The commands accepted in walsender mode are:
19291930
</term>
19301931
<listitem>
19311932
<para>
1932-
The standby's current epoch.
1933+
The epoch of the global xmin xid on the standby.
1934+
</para>
1935+
</listitem>
1936+
</varlistentry>
1937+
<varlistentry>
1938+
<term>
1939+
Int32
1940+
</term>
1941+
<listitem>
1942+
<para>
1943+
The lowest catalog_xmin of any replication slots on the standby. Set to 0
1944+
if no catalog_xmin exists on the standby or if hot standby feedback is being
1945+
disabled.
1946+
</para>
1947+
</listitem>
1948+
</varlistentry>
1949+
<varlistentry>
1950+
<term>
1951+
Int32
1952+
</term>
1953+
<listitem>
1954+
<para>
1955+
The epoch of the catalog_xmin xid on the standby.
19331956
</para>
19341957
</listitem>
19351958
</varlistentry>

src/backend/replication/walreceiver.c

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,8 +1175,8 @@ XLogWalRcvSendHSFeedback(bool immed)
11751175
{
11761176
TimestampTz now;
11771177
TransactionId nextXid;
1178-
uint32 nextEpoch;
1179-
TransactionId xmin;
1178+
uint32 xmin_epoch, catalog_xmin_epoch;
1179+
TransactionId xmin, catalog_xmin;
11801180
static TimestampTz sendTime = 0;
11811181
/* initially true so we always send at least one feedback message */
11821182
static bool master_has_standby_xmin = true;
@@ -1221,29 +1221,54 @@ XLogWalRcvSendHSFeedback(bool immed)
12211221
* everything else has been checked.
12221222
*/
12231223
if (hot_standby_feedback)
1224-
xmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_DEFAULT);
1224+
{
1225+
TransactionId slot_xmin;
1226+
1227+
/*
1228+
* Usually GetOldestXmin() would include both global replication slot
1229+
* xmin and catalog_xmin in its calculations, but we want to derive
1230+
* separate values for each of those. So we ask for an xmin that
1231+
* excludes the catalog_xmin.
1232+
*/
1233+
xmin = GetOldestXmin(NULL,
1234+
PROCARRAY_FLAGS_DEFAULT|PROCARRAY_SLOTS_XMIN);
1235+
1236+
ProcArrayGetReplicationSlotXmin(&slot_xmin, &catalog_xmin);
1237+
1238+
if (TransactionIdIsValid(slot_xmin) &&
1239+
TransactionIdPrecedes(slot_xmin, xmin))
1240+
xmin = slot_xmin;
1241+
}
12251242
else
1243+
{
12261244
xmin = InvalidTransactionId;
1245+
catalog_xmin = InvalidTransactionId;
1246+
}
12271247

12281248
/*
12291249
* Get epoch and adjust if nextXid and oldestXmin are different sides of
12301250
* the epoch boundary.
12311251
*/
1232-
GetNextXidAndEpoch(&nextXid, &nextEpoch);
1252+
GetNextXidAndEpoch(&nextXid, &xmin_epoch);
1253+
catalog_xmin_epoch = xmin_epoch;
12331254
if (nextXid < xmin)
1234-
nextEpoch--;
1255+
xmin_epoch --;
1256+
if (nextXid < catalog_xmin)
1257+
catalog_xmin_epoch --;
12351258

1236-
elog(DEBUG2, "sending hot standby feedback xmin %u epoch %u",
1237-
xmin, nextEpoch);
1259+
elog(DEBUG2, "sending hot standby feedback xmin %u epoch %u catalog_xmin %u catalog_xmin_epoch %u",
1260+
xmin, xmin_epoch, catalog_xmin, catalog_xmin_epoch);
12381261

12391262
/* Construct the message and send it. */
12401263
resetStringInfo(&reply_message);
12411264
pq_sendbyte(&reply_message, 'h');
12421265
pq_sendint64(&reply_message, GetCurrentTimestamp());
12431266
pq_sendint(&reply_message, xmin, 4);
1244-
pq_sendint(&reply_message, nextEpoch, 4);
1267+
pq_sendint(&reply_message, xmin_epoch, 4);
1268+
pq_sendint(&reply_message, catalog_xmin, 4);
1269+
pq_sendint(&reply_message, catalog_xmin_epoch, 4);
12451270
walrcv_send(wrconn, reply_message.data, reply_message.len);
1246-
if (TransactionIdIsValid(xmin))
1271+
if (TransactionIdIsValid(xmin) || TransactionIdIsValid(catalog_xmin))
12471272
master_has_standby_xmin = true;
12481273
else
12491274
master_has_standby_xmin = false;

src/backend/replication/walsender.c

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ static void WalSndPrepareWrite(LogicalDecodingContext *ctx, XLogRecPtr lsn, Tran
242242
static void WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, bool last_write);
243243
static XLogRecPtr WalSndWaitForWal(XLogRecPtr loc);
244244
static TimeOffset LagTrackerRead(int head, XLogRecPtr lsn, TimestampTz now);
245+
static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch);
245246

246247
static void XLogRead(char *buf, XLogRecPtr startptr, Size count);
247248

@@ -1756,7 +1757,7 @@ ProcessStandbyReplyMessage(void)
17561757

17571758
/* compute new replication slot xmin horizon if needed */
17581759
static void
1759-
PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin)
1760+
PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin, TransactionId feedbackCatalogXmin)
17601761
{
17611762
bool changed = false;
17621763
ReplicationSlot *slot = MyReplicationSlot;
@@ -1777,6 +1778,14 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin)
17771778
slot->data.xmin = feedbackXmin;
17781779
slot->effective_xmin = feedbackXmin;
17791780
}
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+
}
17801789
SpinLockRelease(&slot->mutex);
17811790

17821791
if (changed)
@@ -1786,60 +1795,93 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin)
17861795
}
17871796
}
17881797

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+
17891833
/*
17901834
* Hot Standby feedback
17911835
*/
17921836
static void
17931837
ProcessStandbyHSFeedbackMessage(void)
17941838
{
1795-
TransactionId nextXid;
1796-
uint32 nextEpoch;
17971839
TransactionId feedbackXmin;
17981840
uint32 feedbackEpoch;
1841+
TransactionId feedbackCatalogXmin;
1842+
uint32 feedbackCatalogEpoch;
17991843

18001844
/*
18011845
* 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.
18031848
*/
18041849
(void) pq_getmsgint64(&reply_message); /* sendTime; not used ATM */
18051850
feedbackXmin = pq_getmsgint(&reply_message, 4);
18061851
feedbackEpoch = pq_getmsgint(&reply_message, 4);
1852+
feedbackCatalogXmin = pq_getmsgint(&reply_message, 4);
1853+
feedbackCatalogEpoch = pq_getmsgint(&reply_message, 4);
18071854

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",
18091856
feedbackXmin,
1810-
feedbackEpoch);
1857+
feedbackEpoch,
1858+
feedbackCatalogXmin,
1859+
feedbackCatalogEpoch);
18111860

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))
18141867
{
18151868
MyPgXact->xmin = InvalidTransactionId;
18161869
if (MyReplicationSlot != NULL)
1817-
PhysicalReplicationSlotNewXmin(feedbackXmin);
1870+
PhysicalReplicationSlotNewXmin(feedbackXmin, feedbackCatalogXmin);
18181871
return;
18191872
}
18201873

18211874
/*
18221875
* Check that the provided xmin/epoch are sane, that is, not in the future
18231876
* 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.
18271877
*/
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;
18401881

1841-
if (!TransactionIdPrecedesOrEquals(feedbackXmin, nextXid))
1842-
return; /* epoch OK, but it's wrapped around */
1882+
if (TransactionIdIsNormal(feedbackCatalogXmin) &&
1883+
!TransactionIdInRecentPast(feedbackCatalogXmin, feedbackCatalogEpoch))
1884+
return;
18431885

18441886
/*
18451887
* Set the WalSender's xmin equal to the standby's requested xmin, so that
@@ -1864,15 +1906,23 @@ ProcessStandbyHSFeedbackMessage(void)
18641906
* already since a VACUUM could have just finished calling GetOldestXmin.)
18651907
*
18661908
* 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.
18681912
*
18691913
* XXX: It might make sense to generalize the ephemeral slot concept and
18701914
* always use the slot mechanism to handle the feedback xmin.
18711915
*/
18721916
if (MyReplicationSlot != NULL) /* XXX: persistency configurable? */
1873-
PhysicalReplicationSlotNewXmin(feedbackXmin);
1917+
PhysicalReplicationSlotNewXmin(feedbackXmin, feedbackCatalogXmin);
18741918
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+
}
18761926
}
18771927

18781928
/*

src/backend/storage/ipc/procarray.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,10 @@ TransactionIdIsActive(TransactionId xid)
12641264
* corresponding flags is set. Typically, if you want to ignore ones with
12651265
* PROC_IN_VACUUM flag, you can use PROCARRAY_FLAGS_VACUUM.
12661266
*
1267+
* PROCARRAY_SLOTS_XMIN causes GetOldestXmin to ignore the xmin and
1268+
* catalog_xmin of any replication slots that exist in the system when
1269+
* calculating the oldest xmin.
1270+
*
12671271
* This is used by VACUUM to decide which deleted tuples must be preserved in
12681272
* the passed in table. For shared relations backends in all databases must be
12691273
* considered, but for non-shared relations that's not required, since only
@@ -1342,7 +1346,7 @@ GetOldestXmin(Relation rel, int flags)
13421346
volatile PGPROC *proc = &allProcs[pgprocno];
13431347
volatile PGXACT *pgxact = &allPgXact[pgprocno];
13441348

1345-
if (pgxact->vacuumFlags & flags)
1349+
if (pgxact->vacuumFlags & (flags & PROCARRAY_PROC_FLAGS_MASK))
13461350
continue;
13471351

13481352
if (allDbs ||
@@ -1418,7 +1422,8 @@ GetOldestXmin(Relation rel, int flags)
14181422
/*
14191423
* Check whether there are replication slots requiring an older xmin.
14201424
*/
1421-
if (TransactionIdIsValid(replication_slot_xmin) &&
1425+
if (!(flags & PROCARRAY_SLOTS_XMIN) &&
1426+
TransactionIdIsValid(replication_slot_xmin) &&
14221427
NormalTransactionIdPrecedes(replication_slot_xmin, result))
14231428
result = replication_slot_xmin;
14241429

@@ -1428,7 +1433,8 @@ GetOldestXmin(Relation rel, int flags)
14281433
* possible. We need to do so if we're computing the global limit (rel =
14291434
* NULL) or if the passed relation is a catalog relation of some kind.
14301435
*/
1431-
if ((rel == NULL ||
1436+
if (!(flags & PROCARRAY_SLOTS_XMIN) &&
1437+
(rel == NULL ||
14321438
RelationIsAccessibleInLogicalDecoding(rel)) &&
14331439
TransactionIdIsValid(replication_slot_catalog_xmin) &&
14341440
NormalTransactionIdPrecedes(replication_slot_catalog_xmin, result))

src/include/storage/proc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@ struct XidCache
4444
*
4545
* Note: If you modify these flags, you need to modify PROCARRAY_XXX flags
4646
* in src/include/storage/procarray.h.
47+
*
48+
* PROC_RESERVED may later be assigned for use in vacuumFlags, but its value is
49+
* used for PROCARRAY_SLOTS_XMIN in procarray.h, so GetOldestXmin won't be able
50+
* to match and ignore processes with this flag set.
4751
*/
4852
#define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */
4953
#define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */
5054
#define PROC_IN_ANALYZE 0x04 /* currently running analyze */
5155
#define PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */
5256
#define PROC_IN_LOGICAL_DECODING 0x10 /* currently doing logical
5357
* decoding outside xact */
58+
#define PROC_RESERVED 0x20 /* reserved for procarray */
5459

5560
/* flags reset at EOXact */
5661
#define PROC_VACUUM_STATE_MASK \

src/include/storage/procarray.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
#define PROCARRAY_LOGICAL_DECODING_FLAG 0x10 /* currently doing logical
3333
* decoding outside xact */
3434

35+
#define PROCARRAY_SLOTS_XMIN 0x20 /* replication slot xmin,
36+
* catalog_xmin */
37+
/*
38+
* Only flags in PROCARRAY_PROC_FLAGS_MASK are considered when matching
39+
* PGXACT->vacuumFlags. Other flags are used for different purposes and
40+
* have no corresponding PROC flag equivalent.
41+
*/
42+
#define PROCARRAY_PROC_FLAGS_MASK (PROCARRAY_VACUUM_FLAG | \
43+
PROCARRAY_ANALYZE_FLAG | \
44+
PROCARRAY_LOGICAL_DECODING_FLAG)
45+
3546
/* Use the following flags as an input "flags" to GetOldestXmin function */
3647
/* Consider all backends except for logical decoding ones which manage xmin separately */
3748
#define PROCARRAY_FLAGS_DEFAULT PROCARRAY_LOGICAL_DECODING_FLAG

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy