Hi all, On Tue, Jul 28, 2026 at 12:16 AM Paul Kim <[email protected]> wrote: > > Hi, > > While comparing the vacuum loops of the various index AMs I noticed that > gistvacuum_delete_empty_pages() -- the second pass of a GiST vacuum that > unlinks empty leaf pages -- does not call vacuum_delay_point(), even > though it reads a buffer and issues WAL-logged page deletions for every > internal page it revisits. The other page-scanning loops in > gistvacuum.c, gistvacuumscan() and the recursion in gistvacuumpage(), > both call it. > > As a result this phase of a GiST vacuum ignores the cost-based vacuum > delay entirely, and only reacts to query cancellation when a buffer read > happens to perform I/O. On a large GiST index with many emptied leaf > pages this loop can end up walking every internal page of the index. > > The attached patch adds a single vacuum_delay_point(false) at the top of > the loop, where no buffer lock is held, matching the sibling idiom. The > function has lacked the call since it was introduced in 7df159a620b. > > This is long-standing rather than a recent regression, so I'll leave the > question of back-patching to a committer. Note that on branches before > 18 vacuum_delay_point() takes no argument (the bool was added by > e5b0b0ce150), so a back-patch would drop the "false". >
Thank you for the patch. I reviewed and tested the v1 patch. I was able to reproduce the execution path by creating a large GiST index, deleting most of the tuples to generate empty pages, and running VACUUM (VERBOSE). The cleanup phase entered gistvacuum_delete_empty_pages(), and with temporary instrumentation in the function gistvacuum_delete_empty_pages() in gistvacuum.c. I confirmed that the newly added vacuum_delay_point(false) is invoked repeatedly while scanning internal pages. VACUUM (VERBOSE) t; index scan needed: 63058 pages from table (99.00% of total) had 9900000 dead item identifiers removed index "gist_idx": pages: 54352 in total, 53224 newly deleted, 53224 currently deleted, 0 reusable Logfile: LOG: checkpoint starting: wal LOG: Entered gistvacuum_delete_empty_pages CONTEXT: while cleaning up index "gist_idx" of relation "public.t" STATEMENT: VACUUM (VERBOSE) t; LOG: Called vacuum_delay_point() in GiST empty-page deletion The patch ensures that the empty-page deletion pass now periodically invokes vacuum_delay_point(), making its behavior consistent with other long-running VACUUM loops. I did not observe any functional issues or regressions during testing. The patch looks good to me. Regards, Solai
