From adca67867ff5f37ee8c97879a1ec5dc2bf0150f0 Mon Sep 17 00:00:00 2001
From: Shayon Mukherjee <shayonj@gmail.com>
Date: Mon, 5 May 2025 15:32:41 -0400
Subject: [PATCH v18] Limit VACUUM truncation retries after scan interruption

---
 src/backend/access/heap/vacuumlazy.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index f28326bad0..a91c1b97cb 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -179,6 +179,8 @@
 #define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL		20	/* ms */
 #define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL		50	/* ms */
 #define VACUUM_TRUNCATE_LOCK_TIMEOUT			5000	/* ms */
+/* Max retries for the truncation attempt if the backward scan is interrupted */
+#define VACUUM_TRUNCATE_INTERRUPTION_MAX_RETRIES	3
 
 /*
  * Threshold that controls whether we bypass index vacuuming and heap
@@ -3213,6 +3215,7 @@ lazy_truncate_heap(LVRelState *vacrel)
 	BlockNumber new_rel_pages;
 	bool		lock_waiter_detected;
 	int			lock_retry;
+	int			truncate_interruption_retry_count = 0;
 
 	/* Report that we are now truncating */
 	pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
@@ -3227,6 +3230,19 @@ lazy_truncate_heap(LVRelState *vacrel)
 	 */
 	do
 	{
+		/*
+		 * Check if we've retried too many times due to interruptions. We
+		 * check here to allow at least one full attempt even if
+		 * max_truncate_retries is set low.
+		 */
+		if (truncate_interruption_retry_count >= VACUUM_TRUNCATE_INTERRUPTION_MAX_RETRIES)
+		{
+			ereport(vacrel->verbose ? INFO : DEBUG2,
+					(errmsg("table \"%s\": stopping truncate after %d retries due to repeated conflicting lock requests",
+							vacrel->relname, truncate_interruption_retry_count)));
+			break;
+		}
+
 		/*
 		 * We need full exclusive lock on the relation in order to do
 		 * truncation. If we can't get it, give up rather than waiting --- we
@@ -3329,6 +3345,14 @@ lazy_truncate_heap(LVRelState *vacrel)
 						vacrel->relname,
 						orig_rel_pages, new_rel_pages)));
 		orig_rel_pages = new_rel_pages;
+
+		/*
+		 * Increment retry count only if we were interrupted and will loop
+		 * again
+		 */
+		if (lock_waiter_detected && new_rel_pages > vacrel->nonempty_pages)
+			truncate_interruption_retry_count++;
+
 	} while (new_rel_pages > vacrel->nonempty_pages && lock_waiter_detected);
 }
 
-- 
2.39.5 (Apple Git-154)

