v4_cleanup_masahiko.patch
text/x-patch
Filename: v4_cleanup_masahiko.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v4
| File | + | − |
|---|---|---|
| src/backend/access/heap/vacuumlazy.c | 7 | 8 |
| src/backend/replication/slot.c | 80 | 105 |
| src/include/replication/slot.h | 2 | 3 |
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 809061110c7..bc9360b9743 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -805,16 +805,15 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
* vacuum's ability to freeze and remove dead tuples. Since OldestXmin
* already covers the slot xmin/catalog_xmin values, pass it as a
* preliminary check to avoid additional iteration over all the slots.
- *
- * If at least one slot was invalidated, recompute cutoffs so that this
- * vacuum benefits from the advanced horizon immediately.
- *
- * XXX: Next XID could be returned as output from vacuum_get_cutoffs() but
- * for now we live with an additional ReadNextTransactionId() call.
*/
- if (InvalidateXIDAgedReplicationSlots(vacrel->cutoffs.OldestXmin,
- ReadNextTransactionId()))
+ if (MaybeInvalidateXIDAgedSlots(vacrel->cutoffs.OldestXmin))
+ {
+ /*
+ * Some slots have been invalidated based on their XID age; recompute
+ * the vacuum cutoffs.
+ */
vacrel->aggressive = vacuum_get_cutoffs(rel, params, &vacrel->cutoffs);
+ }
vacrel->rel_pages = orig_rel_pages = RelationGetNumberOfBlocks(rel);
vacrel->vistest = GlobalVisTestFor(rel);
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 8ca5d86fe0e..8296856eefe 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -160,7 +160,7 @@ int max_replication_slots = 10; /* the maximum number of replication
int idle_replication_slot_timeout_secs = 0;
/*
- * Invalidate replication slots that have xmin or catalog_xmin greater
+ * Invalidate replication slots that have xmin or catalog_xmin older
* than the specified age; '0' disables it.
*/
int max_slot_xid_age = 0;
@@ -183,8 +183,6 @@ static XLogRecPtr ss_oldest_flush_lsn = InvalidXLogRecPtr;
static void ReplicationSlotShmemExit(int code, Datum arg);
static bool IsSlotForConflictCheck(const char *name);
static void ReplicationSlotDropPtr(ReplicationSlot *slot);
-static bool IsReplicationSlotXIDAged(TransactionId xmin, TransactionId catalog_xmin,
- TransactionId nextXID);
/* internal persistency functions */
static void RestoreSlotFromDisk(const char *name);
@@ -1792,7 +1790,7 @@ ReportSlotInvalidation(ReplicationSlotInvalidationCause cause,
long slot_idle_seconds,
TransactionId xmin,
TransactionId catalog_xmin,
- TransactionId nextXID)
+ TransactionId recentXid)
{
StringInfoData err_detail;
StringInfoData err_hint;
@@ -1845,20 +1843,14 @@ ReportSlotInvalidation(ReplicationSlotInvalidationCause cause,
if (TransactionIdIsValid(xmin))
{
/* translator: %s is a GUC variable name */
- appendStringInfo(&err_detail, _("The slot's xmin %u at next transaction ID %u exceeds the age %d specified by \"%s\"."),
- xmin,
- nextXID,
- max_slot_xid_age,
- "max_slot_xid_age");
+ appendStringInfo(&err_detail, _("The slot's xmin %u is %d transactions old, which exceeds the configured \"%s\" value of %d."),
+ xmin, (int32) (recentXid - xmin), "max_slot_xid_age", max_slot_xid_age);
}
else
{
/* translator: %s is a GUC variable name */
- appendStringInfo(&err_detail, _("The slot's catalog xmin %u at next transaction ID %u exceeds the age %d specified by \"%s\"."),
- catalog_xmin,
- nextXID,
- max_slot_xid_age,
- "max_slot_xid_age");
+ appendStringInfo(&err_detail, _("The slot's xmin %u is %d transactions old, which exceeds the configured \"%s\" value of %d."),
+ catalog_xmin, (int32) (recentXid - catalog_xmin), "max_slot_xid_age", max_slot_xid_age);
}
/* translator: %s is a GUC variable name */
@@ -1905,6 +1897,25 @@ CanInvalidateIdleSlot(ReplicationSlot *s)
!(RecoveryInProgress() && s->data.synced));
}
+/*
+ * Can we invalidate an XID-aged replication slot?
+ *
+ * XID-aged based invalidation is allowed to the given slot when:
+ *
+ * 1. Max XID-age is set
+ * 2. Slot has valid xmin or catalog_xmin
+ * 3. The slot is not being synced from the primary while the server is in
+ * recovery.
+ */
+static inline bool
+CanInvalidateXidAgedSlot(ReplicationSlot *s)
+{
+ return (max_slot_xid_age != 0 &&
+ (TransactionIdIsValid(s->data.xmin) ||
+ TransactionIdIsValid(s->data.catalog_xmin)) &&
+ !(RecoveryInProgress() && s->data.synced));
+}
+
/*
* DetermineSlotInvalidationCause - Determine the cause for which a slot
* becomes invalid among the given possible causes.
@@ -1916,7 +1927,7 @@ static ReplicationSlotInvalidationCause
DetermineSlotInvalidationCause(uint32 possible_causes, ReplicationSlot *s,
XLogRecPtr oldestLSN, Oid dboid,
TransactionId snapshotConflictHorizon,
- TransactionId nextXID,
+ TransactionId recentXid,
TimestampTz *inactive_since, TimestampTz now)
{
Assert(possible_causes != RS_INVAL_NONE);
@@ -1989,9 +2000,23 @@ DetermineSlotInvalidationCause(uint32 possible_causes, ReplicationSlot *s,
}
/* Check if the slot needs to be invalidated due to max_slot_xid_age GUC */
- if ((possible_causes & RS_INVAL_XID_AGE) &&
- IsReplicationSlotXIDAged(s->data.xmin, s->data.catalog_xmin, nextXID))
- return RS_INVAL_XID_AGE;
+ if (possible_causes & RS_INVAL_XID_AGE)
+ {
+ Assert(TransactionIdIsValid(recentXid));
+
+ if (CanInvalidateXidAgedSlot(s))
+ {
+ TransactionId xidLimit;
+
+ xidLimit = TransactionIdRetreatedBy(recentXid, max_slot_xid_age);
+
+ if ((TransactionIdIsValid(s->data.xmin) &&
+ TransactionIdPrecedes(s->data.xmin, xidLimit)) ||
+ (TransactionIdIsValid(s->data.catalog_xmin) &&
+ TransactionIdPrecedes(s->data.catalog_xmin, xidLimit)))
+ return RS_INVAL_XID_AGE;
+ }
+ }
return RS_INVAL_NONE;
}
@@ -2015,7 +2040,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
ReplicationSlot *s,
XLogRecPtr oldestLSN,
Oid dboid, TransactionId snapshotConflictHorizon,
- TransactionId nextXID,
+ TransactionId recentXid,
bool *released_lock_out)
{
int last_signaled_pid = 0;
@@ -2068,7 +2093,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
s, oldestLSN,
dboid,
snapshotConflictHorizon,
- nextXID,
+ recentXid,
&inactive_since,
now);
@@ -2163,7 +2188,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
slotname, restart_lsn,
oldestLSN, snapshotConflictHorizon,
slot_idle_secs, s->data.xmin,
- s->data.catalog_xmin, nextXID);
+ s->data.catalog_xmin, recentXid);
if (MyBackendType == B_STARTUP)
(void) SignalRecoveryConflict(GetPGProcByNumber(active_proc),
@@ -2217,7 +2242,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
slotname, restart_lsn,
oldestLSN, snapshotConflictHorizon,
slot_idle_secs, s->data.xmin,
- s->data.catalog_xmin, nextXID);
+ s->data.catalog_xmin, recentXid);
/* done with this slot for now */
break;
@@ -2259,7 +2284,8 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
bool
InvalidateObsoleteReplicationSlots(uint32 possible_causes,
XLogSegNo oldestSegno, Oid dboid,
- TransactionId snapshotConflictHorizon, TransactionId nextXID)
+ TransactionId snapshotConflictHorizon,
+ TransactionId recentXid)
{
XLogRecPtr oldestLSN;
bool invalidated = false;
@@ -2298,7 +2324,7 @@ restart:
if (InvalidatePossiblyObsoleteSlot(possible_causes, s, oldestLSN,
dboid, snapshotConflictHorizon,
- nextXID, &released_lock))
+ recentXid, &released_lock))
{
Assert(released_lock);
@@ -3330,104 +3356,53 @@ WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn)
ConditionVariableCancelSleep();
}
-/*
- * Check if the passed-in xmin or catalog_xmin have aged beyond the
- * max_slot_xid_age GUC limit relative to nextXID.
- *
- * Returns true if either value exceeds the configured age.
- */
-static bool
-IsReplicationSlotXIDAged(TransactionId xmin, TransactionId catalog_xmin,
- TransactionId nextXID)
-{
- TransactionId cutoffXID;
- bool aged = false;
-
- if (max_slot_xid_age == 0)
- return false;
-
- if (!TransactionIdIsNormal(nextXID))
- return false;
-
- cutoffXID = nextXID - max_slot_xid_age;
-
- /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
- /* this can cause the limit to go backwards by 3, but that's OK */
- if (cutoffXID < FirstNormalTransactionId)
- cutoffXID -= FirstNormalTransactionId;
-
- if (TransactionIdIsNormal(xmin) &&
- TransactionIdPrecedes(xmin, cutoffXID))
- aged = true;
-
- if (TransactionIdIsNormal(catalog_xmin) &&
- TransactionIdPrecedes(catalog_xmin, cutoffXID))
- aged = true;
-
- return aged;
-}
-
/*
* Invalidate replication slots whose XID age exceeds the max_slot_xid_age
* GUC.
*
- * The caller supplies oldestXmin, either computed via
- * GetOldestNonRemovableTransactionId during vacuum, or computed via the
- * minimum of slot xmin values obtained from ProcArrayGetReplicationSlotXmin,
- * and nextXID, the next XID to be assigned used to compute the age.
- *
- * Preliminary checks based on the passed-in oldestXmin and the oldest slot
- * xmin and catalog_xmin are done to avoid unnecessarily iterating over all
- * the slots. If the oldestXmin age does not exceed the GUC then no
- * individual slot can either, so the per-slot scan is skipped. For example,
- * if oldestXmin is 100 and the GUC is 500, every slot's xmin must be >= 100,
- * so none can be older than the GUC. Similarly, if the oldest slot xmin and
- * catalog_xmin from ProcArray are not aged, the per-slot scan is skipped;
- * this can happen when a long-running transaction holds the oldestXmin back.
- *
- * Even if the caller passes an oldestXmin that does not include the slot
- * xmin/catalog_xmin range, there is no risk of incorrect invalidation: each
- * slot's own xmin and catalog_xmin are individually verified against the GUC
- * inside IsReplicationSlotXIDAged(). The only downside is an additional
- * iteration over all the slots.
+ * The oldestXmin is expected to be a XID computed via
+ * GetOldestNonRemovableTransactionId() during vacuum. It is used as cutoffs
+ * for individual slot checks; if its age does not exceed the max_slot_xid_age,
+ * no individual slot can either, so we skip per-slot invalidation check.
*
* Returns true if at least one slot was invalidated.
*/
bool
-InvalidateXIDAgedReplicationSlots(TransactionId oldestXmin, TransactionId nextXID)
+MaybeInvalidateXIDAgedSlots(TransactionId oldestXmin)
{
- TransactionId cutoffXID;
+ TransactionId recentXid;
+ TransactionId xidLimit;
+ TransactionId slot_xmin = InvalidTransactionId;
+ TransactionId slot_catalog_xmin = InvalidTransactionId;
bool invalidated = false;
- if (max_slot_xid_age == 0)
- return false;
+ Assert(TransactionIdIsValid(oldestXmin));
- if (!TransactionIdIsNormal(oldestXmin) || !TransactionIdIsNormal(nextXID))
+ if (max_slot_xid_age == 0)
return false;
- cutoffXID = nextXID - max_slot_xid_age;
+ recentXid = ReadNextTransactionId();
+ xidLimit = TransactionIdRetreatedBy(recentXid, max_slot_xid_age);
- /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
- /* this can cause the limit to go backwards by 3, but that's OK */
- if (!TransactionIdIsNormal(cutoffXID))
- cutoffXID -= FirstNormalTransactionId;
-
- if (TransactionIdPrecedes(oldestXmin, cutoffXID))
- {
- TransactionId slot_xmin;
- TransactionId slot_catalog_xmin;
+ /* oldestXmin is not behind the cutoff; no need to check slots */
+ if (TransactionIdPrecedes(xidLimit, oldestXmin))
+ return false;
- ProcArrayGetReplicationSlotXmin(&slot_xmin, &slot_catalog_xmin);
+ ProcArrayGetReplicationSlotXmin(&slot_xmin, &slot_catalog_xmin);
- if (IsReplicationSlotXIDAged(slot_xmin, slot_catalog_xmin, nextXID))
- {
- invalidated = InvalidateObsoleteReplicationSlots(RS_INVAL_XID_AGE,
- 0,
- InvalidOid,
- InvalidTransactionId,
- nextXID);
- }
- }
+ /*
+ * Invalidate possibly obsolete slots based on XID-age, if either slot's
+ * xmin or catalog_xmin is older than the cutoff.
+ */
+ if ((TransactionIdIsValid(slot_xmin) &&
+ TransactionIdPrecedes(slot_xmin, xidLimit)) ||
+ (TransactionIdIsValid(slot_catalog_xmin) &&
+ TransactionIdPrecedes(slot_catalog_xmin, xidLimit)))
+ invalidated = InvalidateObsoleteReplicationSlots(RS_INVAL_XID_AGE,
+ 0,
+ InvalidOid,
+ InvalidTransactionId,
+ recentXid);
return invalidated;
}
diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h
index 83cf8438724..c483f0f531b 100644
--- a/src/include/replication/slot.h
+++ b/src/include/replication/slot.h
@@ -371,7 +371,8 @@ extern bool InvalidateObsoleteReplicationSlots(uint32 possible_causes,
XLogSegNo oldestSegno,
Oid dboid,
TransactionId snapshotConflictHorizon,
- TransactionId nextXID);
+ TransactionId recentXid);
+extern bool MaybeInvalidateXIDAgedSlots(TransactionId oldestXmin);
extern ReplicationSlot *SearchNamedReplicationSlot(const char *name, bool need_lock);
extern int ReplicationSlotIndex(ReplicationSlot *slot);
extern bool ReplicationSlotName(int index, Name name);
@@ -391,6 +392,4 @@ extern bool SlotExistsInSyncStandbySlots(const char *slot_name);
extern bool StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel);
extern void WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn);
-extern bool InvalidateXIDAgedReplicationSlots(TransactionId oldestXmin, TransactionId nextXID);
-
#endif /* SLOT_H */