v14-0001-Use-streaming-I-O-in-VACUUM-s-first-phase.patch
text/x-patch
Filename: v14-0001-Use-streaming-I-O-in-VACUUM-s-first-phase.patch
Type: text/x-patch
Part: 1
Patch
Format: format-patch
Series: patch v14-0001
Subject: Use streaming I/O in VACUUM's first phase
| File | + | − |
|---|---|---|
| src/backend/access/heap/vacuumlazy.c | 62 | 38 |
From 6d61028e0900d6b96bfe72cb0c262e22b8bde39a Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 5 Feb 2025 17:21:41 -0500
Subject: [PATCH v14 1/2] Use streaming I/O in VACUUM's first phase
Now vacuum's first phase, which HOT-prunes and records the TIDs of
non-removable dead tuples, uses the streaming read API by converting
heap_vac_scan_next_block() to a read stream callback.
Discussion: https://postgr.es/m/CA%2BhUKGKN3oy0bN_3yv8hd78a4%2BM1tJC9z7mD8%2Bf%2ByA%2BGeoFUwQ%40mail.gmail.com
---
src/backend/access/heap/vacuumlazy.c | 100 +++++++++++++++++----------
1 file changed, 62 insertions(+), 38 deletions(-)
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 075af385cd1..70351de403d 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -108,6 +108,7 @@
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
+#include "storage/read_stream.h"
#include "utils/lsyscache.h"
#include "utils/pg_rusage.h"
#include "utils/timestamp.h"
@@ -296,8 +297,9 @@ typedef struct LVSavedErrInfo
/* non-export function prototypes */
static void lazy_scan_heap(LVRelState *vacrel);
-static bool heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
- bool *all_visible_according_to_vm);
+static BlockNumber heap_vac_scan_next_block(ReadStream *stream,
+ void *callback_private_data,
+ void *per_buffer_data);
static void find_next_unskippable_block(LVRelState *vacrel, bool *skipsallvis);
static bool lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf,
BlockNumber blkno, Page page,
@@ -907,10 +909,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
static void
lazy_scan_heap(LVRelState *vacrel)
{
+ ReadStream *stream;
BlockNumber rel_pages = vacrel->rel_pages,
- blkno,
+ blkno = 0,
next_fsm_block_to_vacuum = 0;
- bool all_visible_according_to_vm;
+ bool *all_visible_according_to_vm = NULL;
Buffer vmbuffer = InvalidBuffer;
const int initprog_index[] = {
@@ -926,26 +929,27 @@ lazy_scan_heap(LVRelState *vacrel)
initprog_val[2] = vacrel->dead_items_info->max_bytes;
pgstat_progress_update_multi_param(3, initprog_index, initprog_val);
+ stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE,
+ vacrel->bstrategy,
+ vacrel->rel,
+ MAIN_FORKNUM,
+ heap_vac_scan_next_block,
+ vacrel,
+ sizeof(bool));
+
/* Initialize for the first heap_vac_scan_next_block() call */
vacrel->current_block = InvalidBlockNumber;
vacrel->next_unskippable_block = InvalidBlockNumber;
vacrel->next_unskippable_allvis = false;
vacrel->next_unskippable_vmbuffer = InvalidBuffer;
- while (heap_vac_scan_next_block(vacrel, &blkno, &all_visible_according_to_vm))
+ while (true)
{
Buffer buf;
Page page;
bool has_lpdead_items;
bool got_cleanup_lock = false;
- vacrel->scanned_pages++;
-
- /* Report as block scanned, update error traceback information */
- pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
- update_vacuum_error_info(vacrel, NULL, VACUUM_ERRCB_PHASE_SCAN_HEAP,
- blkno, InvalidOffsetNumber);
-
vacuum_delay_point();
/*
@@ -986,7 +990,8 @@ lazy_scan_heap(LVRelState *vacrel)
/*
* Vacuum the Free Space Map to make newly-freed space visible on
- * upper-level FSM pages. Note we have not yet processed blkno.
+ * upper-level FSM pages. Note that blkno is the previously
+ * processed block.
*/
FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
blkno);
@@ -997,6 +1002,24 @@ lazy_scan_heap(LVRelState *vacrel)
PROGRESS_VACUUM_PHASE_SCAN_HEAP);
}
+ buf = read_stream_next_buffer(stream, (void **) &all_visible_according_to_vm);
+
+ if (!BufferIsValid(buf))
+ break;
+
+ Assert(all_visible_according_to_vm);
+ CheckBufferIsPinnedOnce(buf);
+ page = BufferGetPage(buf);
+
+ vacrel->scanned_pages++;
+
+ blkno = BufferGetBlockNumber(buf);
+
+ /* Report as block scanned, update error traceback information */
+ pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
+ update_vacuum_error_info(vacrel, NULL, VACUUM_ERRCB_PHASE_SCAN_HEAP,
+ blkno, InvalidOffsetNumber);
+
/*
* Pin the visibility map page in case we need to mark the page
* all-visible. In most cases this will be very cheap, because we'll
@@ -1004,10 +1027,6 @@ lazy_scan_heap(LVRelState *vacrel)
*/
visibilitymap_pin(vacrel->rel, blkno, &vmbuffer);
- buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
- vacrel->bstrategy);
- page = BufferGetPage(buf);
-
/*
* We need a buffer cleanup lock to prune HOT chains and defragment
* the page in lazy_scan_prune. But when it's not possible to acquire
@@ -1063,7 +1082,7 @@ lazy_scan_heap(LVRelState *vacrel)
*/
if (got_cleanup_lock)
lazy_scan_prune(vacrel, buf, blkno, page,
- vmbuffer, all_visible_according_to_vm,
+ vmbuffer, *all_visible_according_to_vm,
&has_lpdead_items);
/*
@@ -1117,7 +1136,7 @@ lazy_scan_heap(LVRelState *vacrel)
ReleaseBuffer(vmbuffer);
/* report that everything is now scanned */
- pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
+ pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, rel_pages);
/* now we can compute the new value for pg_class.reltuples */
vacrel->new_live_tuples = vac_estimate_reltuples(vacrel->rel, rel_pages,
@@ -1132,6 +1151,8 @@ lazy_scan_heap(LVRelState *vacrel)
Max(vacrel->new_live_tuples, 0) + vacrel->recently_dead_tuples +
vacrel->missed_dead_tuples;
+ read_stream_end(stream);
+
/*
* Do index vacuuming (call each index's ambulkdelete routine), then do
* related heap vacuuming
@@ -1143,11 +1164,11 @@ lazy_scan_heap(LVRelState *vacrel)
* Vacuum the remainder of the Free Space Map. We must do this whether or
* not there were indexes, and whether or not we bypassed index vacuuming.
*/
- if (blkno > next_fsm_block_to_vacuum)
- FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum, blkno);
+ if (rel_pages > next_fsm_block_to_vacuum)
+ FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum, rel_pages);
/* report all blocks vacuumed */
- pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno);
+ pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, rel_pages);
/* Do final index cleanup (call each index's amvacuumcleanup routine) */
if (vacrel->nindexes > 0 && vacrel->do_index_cleanup)
@@ -1157,14 +1178,14 @@ lazy_scan_heap(LVRelState *vacrel)
/*
* heap_vac_scan_next_block() -- get next block for vacuum to process
*
- * lazy_scan_heap() calls here every time it needs to get the next block to
- * prune and vacuum. The function uses the visibility map, vacuum options,
- * and various thresholds to skip blocks which do not need to be processed and
- * sets blkno to the next block to process.
+ * The streaming read callback invokes heap_vac_scan_next_block() every time
+ * lazy_scan_heap() needs the next block to prune and vacuum. The function
+ * uses the visibility map, vacuum options, and various thresholds to skip
+ * blocks which do not need to be processed and returns the next block to
+ * process or InvalidBlockNumber if there are no remaining blocks.
*
- * The block number and visibility status of the next block to process are set
- * in *blkno and *all_visible_according_to_vm. The return value is false if
- * there are no further blocks to process.
+ * The visibility status of the next block to process is set in the
+ * per_buffer_data.
*
* vacrel is an in/out parameter here. Vacuum options and information about
* the relation are read. vacrel->skippedallvis is set if we skip a block
@@ -1172,11 +1193,14 @@ lazy_scan_heap(LVRelState *vacrel)
* relfrozenxid in that case. vacrel also holds information about the next
* unskippable block, as bookkeeping for this function.
*/
-static bool
-heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
- bool *all_visible_according_to_vm)
+static BlockNumber
+heap_vac_scan_next_block(ReadStream *stream,
+ void *callback_private_data,
+ void *per_buffer_data)
{
BlockNumber next_block;
+ LVRelState *vacrel = callback_private_data;
+ bool *all_visible_according_to_vm = per_buffer_data;
/* relies on InvalidBlockNumber + 1 overflowing to 0 on first call */
next_block = vacrel->current_block + 1;
@@ -1189,8 +1213,8 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
ReleaseBuffer(vacrel->next_unskippable_vmbuffer);
vacrel->next_unskippable_vmbuffer = InvalidBuffer;
}
- *blkno = vacrel->rel_pages;
- return false;
+ vacrel->current_block = vacrel->rel_pages;
+ return InvalidBlockNumber;
}
/*
@@ -1239,9 +1263,9 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
* but chose not to. We know that they are all-visible in the VM,
* otherwise they would've been unskippable.
*/
- *blkno = vacrel->current_block = next_block;
+ vacrel->current_block = next_block;
*all_visible_according_to_vm = true;
- return true;
+ return vacrel->current_block;
}
else
{
@@ -1251,9 +1275,9 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
*/
Assert(next_block == vacrel->next_unskippable_block);
- *blkno = vacrel->current_block = next_block;
+ vacrel->current_block = next_block;
*all_visible_according_to_vm = vacrel->next_unskippable_allvis;
- return true;
+ return vacrel->current_block;
}
}
--
2.34.1