Alexander Lakhin <[email protected]> wrote:

> Could you please look at an assertion failure produced by the following
> script, starting from 0d3dba38c:?
> createdb db1
> createdb db2
> 
> echo "
> CREATE TABLE t0 (a text);
> BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
> INSERT INTO t0 VALUES ('a');
> SELECT pg_sleep(1);
> " | psql db1 &
> 
> echo "
> CREATE TABLE t1 (id int PRIMARY KEY);
> CREATE TABLE t2 (id int PRIMARY KEY, a TEXT, FOREIGN KEY (id) REFERENCES t1);
> SET min_parallel_table_scan_size = 1;
> REPACK (CONCURRENTLY) t2;
> " | psql db2
> wait
> 
> It triggers for me:
> TRAP: failed Assert("TransactionIdPrecedesOrEquals(TransactionXmin, 
> RecentXmin)"), File: "procarray.c", Line: 2071, PID: 3529520

Attached is a fix that works for me.

Nevertheless, REPACK (CONCURRENTLY) in your test goes ahead only due to commit
0d3dba38c7, which will probably be reverted [1]. Then REPACK will wait for the
transaction in the other database (db1) to complete before it can actually
start.

Thanks for the report!

[1] 
https://www.postgresql.org/message-id/cdgw4sbbfcgk6du3iv54r2dgiy4tfywoklbotlmj4irxavdcr3@glxfw5jj277q

-- 
Antonin Houska
Web: https://www.cybertec-postgresql.com

>From 3fd0a70566f42d695b92c1cb2d26d3dbd5918a58 Mon Sep 17 00:00:00 2001
From: Antonin Houska <[email protected]>
Date: Fri, 10 Apr 2026 12:17:36 +0200
Subject: [PATCH] Do not push the active snapshot for copy_table_data before
 it's needed.

make_new_heap() can launch parallel workers when building index on TOAST
relation. If the snapshot for copy_table_data() is already the active
snapshot, the workers can incorrectly consider it their transaction snapshot.
---
 src/backend/commands/repack.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 58e3867246f..08fd132a4e3 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -1013,8 +1013,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose,
 		 * Wait until the worker has the initial snapshot and retrieve it.
 		 */
 		snapshot = get_initial_snapshot(decoding_worker);
-
-		PushActiveSnapshot(snapshot);
 	}
 
 	/* for CLUSTER or REPACK USING INDEX, mark the index as the one to use */
@@ -1038,6 +1036,8 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose,
 	NewHeap = table_open(OIDNewHeap, NoLock);
 
 	/* Copy the heap data into the new table in the desired order */
+	if (snapshot)
+		PushActiveSnapshot(snapshot);
 	copy_table_data(NewHeap, OldHeap, index, snapshot, verbose,
 					&swap_toast_by_content, &frozenXid, &cutoffMulti);
 
-- 
2.47.3

Reply via email to