From 7f994f1ad50a64cf18a69d0c5063a7612b58dec9 Mon Sep 17 00:00:00 2001
From: Mostafa <mostafa.nabil.nafie@gmail.com>
Date: Sat, 22 Aug 2026 17:25:50 +0300
Subject: [PATCH] Check for interrupts between hash index vacuum buckets

hashbulkdelete() acquires a cleanup lock on each bucket's primary
page and holds it for the duration of hashbucketcleanup(), which
walks every page in that bucket's overflow chain.  While that lock
is held, interrupts are held off, so the vacuum_delay_point() call
inside hashbucketcleanup()'s per-page loop is ineffective.  The
outer per-bucket loop in hashbulkdelete() had no interrupt check at
all, so a pending shutdown or query cancel was not honored until the
entire index had been scanned, which can take minutes on a table
with many or large buckets.

Add a vacuum_delay_point() call at the top of the per-bucket loop,
before any lock is acquired, so a pending interrupt is noticed at
the next bucket boundary instead of only after the whole index scan
completes.  Verified with a manual reproduction: cancelling a VACUUM
mid-"vacuuming indexes" phase on a hash index with many buckets went
from ~2.1s to honor the cancel down to ~0.01s with this change.

This does not address the case of a single bucket with a very long
overflow chain, where the checkpoint added here still cannot fire
until that bucket's own cleanup finishes; that would require
changing how hashbucketcleanup()'s lock chaining works, and is left
as a follow-up.

Reported-by: Sergei Kornilov <sk@zsrv.org>
Discussion: https://postgr.es/m/19628-c2b17d358181a1ea@postgresql.org
---
 src/backend/access/hash/hash.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index b2e34d2d45e..796a178f3ec 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -562,6 +562,12 @@ bucket_loop:
 		Page		page;
 		bool		split_cleanup = false;
 
+		/*
+		 * Check for interrupts before acquiring the cleanup lock on the next
+		 * bucket.
+		 */
+		vacuum_delay_point(false);
+
 		/* Get address of bucket's start page */
 		bucket_blkno = BUCKET_TO_BLKNO(cachedmetap, cur_bucket);
 
-- 
2.43.0

