vX-0001-Handle-VACUUM-interaction-with-REPACK-CONCURRENTL.patch
application/octet-stream
Filename: vX-0001-Handle-VACUUM-interaction-with-REPACK-CONCURRENTL.patch
Type: application/octet-stream
Part: 0
Message:
Re: Adding REPACK [concurrently]
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: format-patch
Series: patch 0001
Subject: Handle VACUUM interaction with REPACK CONCURRENTLY and separate catalog/data horizons, based on d9d076222f5
| File | + | − |
|---|---|---|
| src/backend/commands/cluster.c | 31 | 1 |
| src/backend/storage/ipc/procarray.c | 29 | 7 |
| src/include/storage/proc.h | 4 | 2 |
From 2edaf6d9a427c9f683989bd8eb1ed4da9579541a Mon Sep 17 00:00:00 2001
From: Mikhail Nikalayeu <mihailnikalayeu@gmail.com>
Date: Sun, 1 Feb 2026 14:21:41 +0100
Subject: [PATCH vX] Handle VACUUM interaction with REPACK CONCURRENTLY and
separate catalog/data horizons, based on d9d076222f5
---
src/backend/commands/cluster.c | 32 ++++++++++++++++++++++++-
src/backend/storage/ipc/procarray.c | 36 +++++++++++++++++++++++------
src/include/storage/proc.h | 6 +++--
3 files changed, 64 insertions(+), 10 deletions(-)
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 403bdcda32e..5afd6e8088f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -292,7 +292,7 @@ static void export_snapshot(Snapshot snapshot,
DecodingWorkerShared *shared);
static void ProcessRepackMessage(StringInfo msg);
static const char *RepackCommandAsString(RepackCommand cmd);
-
+static void set_in_repack_procflags(void);
#define REPL_PLUGIN_NAME "pgoutput_repack"
@@ -355,6 +355,14 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
parser_errposition(pstate, opt->location));
}
+ if ((params.options & CLUOPT_CONCURRENT) != 0)
+ {
+ InvalidateCatalogSnapshot();
+ PopActiveSnapshot();
+ set_in_repack_procflags();
+ PushActiveSnapshot(GetTransactionSnapshot());
+ }
+
/*
* Determine the lock mode expected by cluster_rel().
*
@@ -509,6 +517,12 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
continue;
}
+ if ((params.options & CLUOPT_CONCURRENT) != 0)
+ {
+ InvalidateCatalogSnapshot();
+ set_in_repack_procflags();
+ }
+
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
@@ -4357,3 +4371,19 @@ ProcessRepackMessage(StringInfo msg)
}
}
}
+
+static void
+set_in_repack_procflags(void)
+{
+ /*
+ * This should only be called before installing xid or xmin in MyProc;
+ * otherwise, concurrent processes could see an Xmin that moves backwards.
+ */
+ Assert(MyProc->xid == InvalidTransactionId &&
+ MyProc->xmin == InvalidTransactionId);
+
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ MyProc->statusFlags |= PROC_IN_REPACK;
+ ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+ LWLockRelease(ProcArrayLock);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 6be565155ab..9cede06338a 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -1642,7 +1642,12 @@ TransactionIdIsInProgress(TransactionId xid)
* relations that's not required, since only backends in my own database could
* ever see the tuples in them. Also, we can ignore concurrently running lazy
* VACUUMs because (a) they must be working on other tables, and (b) they
- * don't need to do snapshot-based lookups.
+ * don't need to do snapshot-based lookups. Similarly, for the non-catalog
+ * horizon, we can ignore REPACK CONCURRENTLY for the
+ * same reasons and because they can't run in transaction blocks. (They are
+ * not possible to ignore for catalogs, because REPACK do some catalog
+ * operations.) Do note that this means that REPACK must use a lock level
+ * that conflicts with VACUUM.
*
* This also computes a horizon used to truncate pg_subtrans. For that
* backends in all databases have to be considered, and concurrently running
@@ -1689,9 +1694,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
bool in_recovery = RecoveryInProgress();
TransactionId *other_xids = ProcGlobal->xids;
- /* inferred after ProcArrayLock is released */
- h->catalog_oldest_nonremovable = InvalidTransactionId;
-
LWLockAcquire(ProcArrayLock, LW_SHARED);
h->latest_completed = TransamVariables->latestCompletedXid;
@@ -1711,6 +1713,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
h->oldest_considered_running = initial;
h->shared_oldest_nonremovable = initial;
+ h->catalog_oldest_nonremovable = initial;
h->data_oldest_nonremovable = initial;
/*
@@ -1809,11 +1812,26 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
(statusFlags & PROC_AFFECTS_ALL_HORIZONS) ||
in_recovery)
{
- h->data_oldest_nonremovable =
- TransactionIdOlder(h->data_oldest_nonremovable, xmin);
+ /*
+ * We can ignore this backend if it's running REPACK
+ * CONCURRENTLY - but
+ * only on vacuums of user-defined tables.
+ */
+ if (!(statusFlags & PROC_IN_REPACK))
+ h->data_oldest_nonremovable =
+ TransactionIdOlder(h->data_oldest_nonremovable, xmin);
+
+ /* Catalog tables need to consider all backends in this db */
+ h->catalog_oldest_nonremovable =
+ TransactionIdOlder(h->catalog_oldest_nonremovable, xmin);
+
}
}
+ /* catalog horizon should never be later than data */
+ Assert(TransactionIdPrecedesOrEquals(h->catalog_oldest_nonremovable,
+ h->data_oldest_nonremovable));
+
/*
* If in recovery fetch oldest xid in KnownAssignedXids, will be applied
* after lock is released.
@@ -1835,6 +1853,8 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
TransactionIdOlder(h->shared_oldest_nonremovable, kaxmin);
h->data_oldest_nonremovable =
TransactionIdOlder(h->data_oldest_nonremovable, kaxmin);
+ h->catalog_oldest_nonremovable =
+ TransactionIdOlder(h->catalog_oldest_nonremovable, kaxmin);
/* temp relations cannot be accessed in recovery */
}
@@ -1862,7 +1882,9 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
h->shared_oldest_nonremovable =
TransactionIdOlder(h->shared_oldest_nonremovable,
h->slot_catalog_xmin);
- h->catalog_oldest_nonremovable = h->data_oldest_nonremovable;
+ h->catalog_oldest_nonremovable =
+ TransactionIdOlder(h->catalog_oldest_nonremovable,
+ h->slot_xmin);
h->catalog_oldest_nonremovable =
TransactionIdOlder(h->catalog_oldest_nonremovable,
h->slot_catalog_xmin);
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 039bc8353be..5396680d2b9 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -66,16 +66,18 @@ struct XidCache
#define PROC_AFFECTS_ALL_HORIZONS 0x20 /* this proc's xmin must be
* included in vacuum horizons
* in all databases */
+#define PROC_IN_REPACK 0x40
+
/* flags reset at EOXact */
#define PROC_VACUUM_STATE_MASK \
- (PROC_IN_VACUUM | PROC_IN_SAFE_IC | PROC_VACUUM_FOR_WRAPAROUND)
+ (PROC_IN_VACUUM | PROC_IN_SAFE_IC | PROC_VACUUM_FOR_WRAPAROUND | PROC_IN_REPACK)
/*
* Xmin-related flags. Make sure any flags that affect how the process' Xmin
* value is interpreted by VACUUM are included here.
*/
-#define PROC_XMIN_FLAGS (PROC_IN_VACUUM | PROC_IN_SAFE_IC)
+#define PROC_XMIN_FLAGS (PROC_IN_VACUUM | PROC_IN_SAFE_IC | PROC_IN_REPACK)
/*
* We allow a limited number of "weak" relation locks (AccessShareLock,
--
2.43.0