diff options
author | Melanie Plageman <melanieplageman@gmail.com> | 2025-03-21 14:05:36 -0400 |
---|---|---|
committer | Melanie Plageman <melanieplageman@gmail.com> | 2025-03-21 14:06:45 -0400 |
commit | 69273b818b1df82c36b2b2acb592db3d0743cc7c (patch) | |
tree | d44ad463450e7d574324ae9dee8d2cd9cd9aa022 /src | |
parent | 3f850c3fc5442084d13122be7f54335e4d017bef (diff) | |
download | postgresql-69273b818b1df82c36b2b2acb592db3d0743cc7c.tar.gz postgresql-69273b818b1df82c36b2b2acb592db3d0743cc7c.zip |
Use streaming read I/O in GiST vacuuming
Like c5c239e26e387 did for btree vacuuming, make GiST vacuum use the
read stream API for sequentially processed pages.
Because it is possible for concurrent insertions to relocate unprocessed
index entries to already vacuumed pages, GiST vacuum must backtrack and
reprocess those pages. These pages are still read with explicit
ReadBuffer() calls.
Author: Andrey M. Borodin <x4mmm@yandex-team.ru>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/EFEBED92-18D1-4C0F-A4EB-CD47072EF071%40yandex-team.ru
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/gist/gistvacuum.c | 84 |
1 files changed, 61 insertions, 23 deletions
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index dd0d9d5006c..20b1bb5dbac 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -22,6 +22,7 @@ #include "miscadmin.h" #include "storage/indexfsm.h" #include "storage/lmgr.h" +#include "storage/read_stream.h" #include "utils/memutils.h" /* Working state needed by gistbulkdelete */ @@ -44,8 +45,7 @@ typedef struct static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state); -static void gistvacuumpage(GistVacState *vstate, BlockNumber blkno, - BlockNumber orig_blkno); +static void gistvacuumpage(GistVacState *vstate, Buffer buffer); static void gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate); static bool gistdeletepage(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, @@ -129,8 +129,9 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, GistVacState vstate; BlockNumber num_pages; bool needLock; - BlockNumber blkno; MemoryContext oldctx; + BlockRangeReadStreamPrivate p; + ReadStream *stream = NULL; /* * Reset fields that track information about the entire index now. This @@ -208,7 +209,14 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, */ needLock = !RELATION_IS_LOCAL(rel); - blkno = GIST_ROOT_BLKNO; + p.current_blocknum = GIST_ROOT_BLKNO; + stream = read_stream_begin_relation(READ_STREAM_FULL, + info->strategy, + rel, + MAIN_FORKNUM, + block_range_read_stream_cb, + &p, + 0); for (;;) { /* Get the current relation length */ @@ -219,13 +227,39 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, UnlockRelationForExtension(rel, ExclusiveLock); /* Quit if we've scanned the whole relation */ - if (blkno >= num_pages) + if (p.current_blocknum >= num_pages) break; - /* Iterate over pages, then loop back to recheck length */ - for (; blkno < num_pages; blkno++) - gistvacuumpage(&vstate, blkno, blkno); + + p.last_exclusive = num_pages; + + /* Iterate over pages, then loop back to recheck relation length */ + while (true) + { + Buffer buf; + + /* call vacuum_delay_point while not holding any buffer lock */ + vacuum_delay_point(false); + + buf = read_stream_next_buffer(stream, NULL); + + if (!BufferIsValid(buf)) + break; + + gistvacuumpage(&vstate, buf); + } + + Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer); + + /* + * We have to reset the read stream to use it again. After returning + * InvalidBuffer, the read stream API won't invoke our callback again + * until the stream has been reset. + */ + read_stream_reset(stream); } + read_stream_end(stream); + /* * If we found any recyclable pages (and recorded them in the FSM), then * forcibly update the upper-level FSM pages to ensure that searchers can @@ -260,34 +294,32 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, /* * gistvacuumpage --- VACUUM one page * - * This processes a single page for gistbulkdelete(). In some cases we - * must go back and re-examine previously-scanned pages; this routine - * recurses when necessary to handle that case. - * - * blkno is the page to process. orig_blkno is the highest block number - * reached by the outer gistvacuumscan loop (the same as blkno, unless we - * are recursing to re-examine a previous page). + * This processes a single page for gistbulkdelete(). `buffer` contains the + * page to process. In some cases we must go back and reexamine + * previously-scanned pages; this routine recurses when necessary to handle + * that case. */ static void -gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno) +gistvacuumpage(GistVacState *vstate, Buffer buffer) { IndexVacuumInfo *info = vstate->info; IndexBulkDeleteCallback callback = vstate->callback; void *callback_state = vstate->callback_state; Relation rel = info->index; - Buffer buffer; + BlockNumber orig_blkno = BufferGetBlockNumber(buffer); Page page; BlockNumber recurse_to; + /* + * orig_blkno is the highest block number reached by the outer + * gistvacuumscan() loop. This will be the same as blkno unless we are + * recursing to reexamine a previous page. + */ + BlockNumber blkno = orig_blkno; + restart: recurse_to = InvalidBlockNumber; - /* call vacuum_delay_point while not holding any buffer lock */ - vacuum_delay_point(false); - - buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, - info->strategy); - /* * We are not going to stay here for a long time, aggressively grab an * exclusive lock. @@ -450,6 +482,12 @@ restart: if (recurse_to != InvalidBlockNumber) { blkno = recurse_to; + + /* check for vacuum delay while not holding any buffer lock */ + vacuum_delay_point(false); + + buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, + info->strategy); goto restart; } } |