From 09f718e6132f0aa8f8fd3851200ee3c7a025c520 Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Sat, 18 Oct 2025 15:24:35 +0800
Subject: [PATCH v1] The SnapBuildPurgeOlderTxn function previously used an
 suboptimal\nmethod to remove old XIDs from the committed.xip array. It
 allocated\na temporary workspace array, copied the surviving elements into
 it, and\nthen copied them back. This incurred the overhead of memory
 allocation\nand multiple data copies.\n\nRefactors the logic to use a
 standard two-pointer, in-place compaction algorithm. \nThis approach filters
 the array in a single pass with no additional memory allocation, \nmaking it
 more efficient in both CPU and memory usage.

---
 src/backend/replication/logical/snapbuild.c | 22 ++++-----------------
 1 file changed, 4 insertions(+), 18 deletions(-)

diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index 98ddee20929..b2e1d41f32a 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -863,39 +863,25 @@ static void
 SnapBuildPurgeOlderTxn(SnapBuild *builder)
 {
 	int			off;
-	TransactionId *workspace;
 	int			surviving_xids = 0;
 
 	/* not ready yet */
 	if (!TransactionIdIsNormal(builder->xmin))
 		return;
 
-	/* TODO: Neater algorithm than just copying and iterating? */
-	workspace =
-		MemoryContextAlloc(builder->context,
-						   builder->committed.xcnt * sizeof(TransactionId));
-
-	/* copy xids that still are interesting to workspace */
+	/* Use in-place compaction to remove xids < xmin */
 	for (off = 0; off < builder->committed.xcnt; off++)
 	{
-		if (NormalTransactionIdPrecedes(builder->committed.xip[off],
-										builder->xmin))
-			;					/* remove */
-		else
-			workspace[surviving_xids++] = builder->committed.xip[off];
+		if (!NormalTransactionIdPrecedes(builder->committed.xip[off],
+										 builder->xmin))
+			builder->committed.xip[surviving_xids++] = builder->committed.xip[off];
 	}
 
-	/* copy workspace back to persistent state */
-	memcpy(builder->committed.xip, workspace,
-		   surviving_xids * sizeof(TransactionId));
-
 	elog(DEBUG3, "purged committed transactions from %u to %u, xmin: %u, xmax: %u",
 		 (uint32) builder->committed.xcnt, (uint32) surviving_xids,
 		 builder->xmin, builder->xmax);
 	builder->committed.xcnt = surviving_xids;
 
-	pfree(workspace);
-
 	/*
 	 * Purge xids in ->catchange as well. The purged array must also be sorted
 	 * in xidComparator order.
-- 
2.51.0

