153
153
#include "storage/bufmgr.h"
154
154
#include "storage/freespace.h"
155
155
#include "storage/lmgr.h"
156
+ #include "storage/read_stream.h"
156
157
#include "utils/lsyscache.h"
157
158
#include "utils/pg_rusage.h"
158
159
#include "utils/timestamp.h"
@@ -423,8 +424,9 @@ typedef struct LVSavedErrInfo
423
424
static void lazy_scan_heap (LVRelState * vacrel );
424
425
static void heap_vacuum_eager_scan_setup (LVRelState * vacrel ,
425
426
VacuumParams * params );
426
- static bool heap_vac_scan_next_block (LVRelState * vacrel , BlockNumber * blkno ,
427
- uint8 * blk_info );
427
+ static BlockNumber heap_vac_scan_next_block (ReadStream * stream ,
428
+ void * callback_private_data ,
429
+ void * per_buffer_data );
428
430
static void find_next_unskippable_block (LVRelState * vacrel , bool * skipsallvis );
429
431
static bool lazy_scan_new_or_empty (LVRelState * vacrel , Buffer buf ,
430
432
BlockNumber blkno , Page page ,
@@ -1174,10 +1176,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
1174
1176
static void
1175
1177
lazy_scan_heap (LVRelState * vacrel )
1176
1178
{
1179
+ ReadStream * stream ;
1177
1180
BlockNumber rel_pages = vacrel -> rel_pages ,
1178
- blkno ,
1181
+ blkno = 0 ,
1179
1182
next_fsm_block_to_vacuum = 0 ;
1180
- uint8 blk_info = 0 ;
1183
+ void * per_buffer_data = NULL ;
1181
1184
BlockNumber orig_eager_scan_success_limit =
1182
1185
vacrel -> eager_scan_remaining_successes ; /* for logging */
1183
1186
Buffer vmbuffer = InvalidBuffer ;
@@ -1201,23 +1204,24 @@ lazy_scan_heap(LVRelState *vacrel)
1201
1204
vacrel -> next_unskippable_eager_scanned = false;
1202
1205
vacrel -> next_unskippable_vmbuffer = InvalidBuffer ;
1203
1206
1204
- while (heap_vac_scan_next_block (vacrel , & blkno , & blk_info ))
1207
+ /* Set up the read stream for vacuum's first pass through the heap */
1208
+ stream = read_stream_begin_relation (READ_STREAM_MAINTENANCE ,
1209
+ vacrel -> bstrategy ,
1210
+ vacrel -> rel ,
1211
+ MAIN_FORKNUM ,
1212
+ heap_vac_scan_next_block ,
1213
+ vacrel ,
1214
+ sizeof (uint8 ));
1215
+
1216
+ while (true)
1205
1217
{
1206
1218
Buffer buf ;
1207
1219
Page page ;
1220
+ uint8 blk_info = 0 ;
1208
1221
bool has_lpdead_items ;
1209
1222
bool vm_page_frozen = false;
1210
1223
bool got_cleanup_lock = false;
1211
1224
1212
- vacrel -> scanned_pages ++ ;
1213
- if (blk_info & VAC_BLK_WAS_EAGER_SCANNED )
1214
- vacrel -> eager_scanned_pages ++ ;
1215
-
1216
- /* Report as block scanned, update error traceback information */
1217
- pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED , blkno );
1218
- update_vacuum_error_info (vacrel , NULL , VACUUM_ERRCB_PHASE_SCAN_HEAP ,
1219
- blkno , InvalidOffsetNumber );
1220
-
1221
1225
vacuum_delay_point (false);
1222
1226
1223
1227
/*
@@ -1229,7 +1233,8 @@ lazy_scan_heap(LVRelState *vacrel)
1229
1233
* one-pass strategy, and the two-pass strategy with the index_cleanup
1230
1234
* param set to 'off'.
1231
1235
*/
1232
- if (vacrel -> scanned_pages % FAILSAFE_EVERY_PAGES == 0 )
1236
+ if (vacrel -> scanned_pages > 0 &&
1237
+ vacrel -> scanned_pages % FAILSAFE_EVERY_PAGES == 0 )
1233
1238
lazy_check_wraparound_failsafe (vacrel );
1234
1239
1235
1240
/*
@@ -1258,28 +1263,45 @@ lazy_scan_heap(LVRelState *vacrel)
1258
1263
1259
1264
/*
1260
1265
* Vacuum the Free Space Map to make newly-freed space visible on
1261
- * upper-level FSM pages. Note we have not yet processed blkno.
1266
+ * upper-level FSM pages. Note that blkno is the previously
1267
+ * processed block.
1262
1268
*/
1263
1269
FreeSpaceMapVacuumRange (vacrel -> rel , next_fsm_block_to_vacuum ,
1264
- blkno );
1270
+ blkno + 1 );
1265
1271
next_fsm_block_to_vacuum = blkno ;
1266
1272
1267
1273
/* Report that we are once again scanning the heap */
1268
1274
pgstat_progress_update_param (PROGRESS_VACUUM_PHASE ,
1269
1275
PROGRESS_VACUUM_PHASE_SCAN_HEAP );
1270
1276
}
1271
1277
1278
+ buf = read_stream_next_buffer (stream , & per_buffer_data );
1279
+
1280
+ /* The relation is exhausted. */
1281
+ if (!BufferIsValid (buf ))
1282
+ break ;
1283
+
1284
+ blk_info = * ((uint8 * ) per_buffer_data );
1285
+ CheckBufferIsPinnedOnce (buf );
1286
+ page = BufferGetPage (buf );
1287
+ blkno = BufferGetBlockNumber (buf );
1288
+
1289
+ vacrel -> scanned_pages ++ ;
1290
+ if (blk_info & VAC_BLK_WAS_EAGER_SCANNED )
1291
+ vacrel -> eager_scanned_pages ++ ;
1292
+
1293
+ /* Report as block scanned, update error traceback information */
1294
+ pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED , blkno );
1295
+ update_vacuum_error_info (vacrel , NULL , VACUUM_ERRCB_PHASE_SCAN_HEAP ,
1296
+ blkno , InvalidOffsetNumber );
1297
+
1272
1298
/*
1273
1299
* Pin the visibility map page in case we need to mark the page
1274
1300
* all-visible. In most cases this will be very cheap, because we'll
1275
1301
* already have the correct page pinned anyway.
1276
1302
*/
1277
1303
visibilitymap_pin (vacrel -> rel , blkno , & vmbuffer );
1278
1304
1279
- buf = ReadBufferExtended (vacrel -> rel , MAIN_FORKNUM , blkno , RBM_NORMAL ,
1280
- vacrel -> bstrategy );
1281
- page = BufferGetPage (buf );
1282
-
1283
1305
/*
1284
1306
* We need a buffer cleanup lock to prune HOT chains and defragment
1285
1307
* the page in lazy_scan_prune. But when it's not possible to acquire
@@ -1439,8 +1461,12 @@ lazy_scan_heap(LVRelState *vacrel)
1439
1461
if (BufferIsValid (vmbuffer ))
1440
1462
ReleaseBuffer (vmbuffer );
1441
1463
1442
- /* report that everything is now scanned */
1443
- pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED , blkno );
1464
+ /*
1465
+ * Report that everything is now scanned. We never skip scanning the last
1466
+ * block in the relation, so we can pass rel_pages here.
1467
+ */
1468
+ pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED ,
1469
+ rel_pages );
1444
1470
1445
1471
/* now we can compute the new value for pg_class.reltuples */
1446
1472
vacrel -> new_live_tuples = vac_estimate_reltuples (vacrel -> rel , rel_pages ,
@@ -1455,6 +1481,8 @@ lazy_scan_heap(LVRelState *vacrel)
1455
1481
Max (vacrel -> new_live_tuples , 0 ) + vacrel -> recently_dead_tuples +
1456
1482
vacrel -> missed_dead_tuples ;
1457
1483
1484
+ read_stream_end (stream );
1485
+
1458
1486
/*
1459
1487
* Do index vacuuming (call each index's ambulkdelete routine), then do
1460
1488
* related heap vacuuming
@@ -1465,49 +1493,56 @@ lazy_scan_heap(LVRelState *vacrel)
1465
1493
/*
1466
1494
* Vacuum the remainder of the Free Space Map. We must do this whether or
1467
1495
* not there were indexes, and whether or not we bypassed index vacuuming.
1496
+ * We can pass rel_pages here because we never skip scanning the last
1497
+ * block of the relation.
1468
1498
*/
1469
- if (blkno > next_fsm_block_to_vacuum )
1470
- FreeSpaceMapVacuumRange (vacrel -> rel , next_fsm_block_to_vacuum , blkno );
1499
+ if (rel_pages > next_fsm_block_to_vacuum )
1500
+ FreeSpaceMapVacuumRange (vacrel -> rel , next_fsm_block_to_vacuum , rel_pages );
1471
1501
1472
1502
/* report all blocks vacuumed */
1473
- pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_VACUUMED , blkno );
1503
+ pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_VACUUMED , rel_pages );
1474
1504
1475
1505
/* Do final index cleanup (call each index's amvacuumcleanup routine) */
1476
1506
if (vacrel -> nindexes > 0 && vacrel -> do_index_cleanup )
1477
1507
lazy_cleanup_all_indexes (vacrel );
1478
1508
}
1479
1509
1480
1510
/*
1481
- * heap_vac_scan_next_block() -- get next block for vacuum to process
1482
- *
1483
- * lazy_scan_heap() calls here every time it needs to get the next block to
1484
- * prune and vacuum. The function uses the visibility map, vacuum options,
1485
- * and various thresholds to skip blocks which do not need to be processed and
1486
- * sets blkno to the next block to process.
1487
- *
1488
- * The block number of the next block to process is set in *blkno and its
1489
- * visibility status and whether or not it was eager scanned is set in
1490
- * *blk_info.
1491
- *
1492
- * The return value is false if there are no further blocks to process.
1493
- *
1494
- * vacrel is an in/out parameter here. Vacuum options and information about
1495
- * the relation are read. vacrel->skippedallvis is set if we skip a block
1496
- * that's all-visible but not all-frozen, to ensure that we don't update
1497
- * relfrozenxid in that case. vacrel also holds information about the next
1498
- * unskippable block, as bookkeeping for this function.
1511
+ * heap_vac_scan_next_block() -- read stream callback to get the next block
1512
+ * for vacuum to process
1513
+ *
1514
+ * Every time lazy_scan_heap() needs a new block to process during its first
1515
+ * phase, it invokes read_stream_next_buffer() with a stream set up to call
1516
+ * heap_vac_scan_next_block() to get the next block.
1517
+ *
1518
+ * heap_vac_scan_next_block() uses the visibility map, vacuum options, and
1519
+ * various thresholds to skip blocks which do not need to be processed and
1520
+ * returns the next block to process or InvalidBlockNumber if there are no
1521
+ * remaining blocks.
1522
+ *
1523
+ * The visibility status of the next block to process and whether or not it
1524
+ * was eager scanned is set in the per_buffer_data.
1525
+ *
1526
+ * callback_private_data contains a reference to the LVRelState, passed to the
1527
+ * read stream API during stream setup. The LVRelState is an in/out parameter
1528
+ * here (locally named `vacrel`). Vacuum options and information about the
1529
+ * relation are read from it. vacrel->skippedallvis is set if we skip a block
1530
+ * that's all-visible but not all-frozen (to ensure that we don't update
1531
+ * relfrozenxid in that case). vacrel also holds information about the next
1532
+ * unskippable block -- as bookkeeping for this function.
1499
1533
*/
1500
- static bool
1501
- heap_vac_scan_next_block (LVRelState * vacrel , BlockNumber * blkno ,
1502
- uint8 * blk_info )
1534
+ static BlockNumber
1535
+ heap_vac_scan_next_block (ReadStream * stream ,
1536
+ void * callback_private_data ,
1537
+ void * per_buffer_data )
1503
1538
{
1504
1539
BlockNumber next_block ;
1540
+ LVRelState * vacrel = callback_private_data ;
1541
+ uint8 blk_info = 0 ;
1505
1542
1506
1543
/* relies on InvalidBlockNumber + 1 overflowing to 0 on first call */
1507
1544
next_block = vacrel -> current_block + 1 ;
1508
1545
1509
- * blk_info = 0 ;
1510
-
1511
1546
/* Have we reached the end of the relation? */
1512
1547
if (next_block >= vacrel -> rel_pages )
1513
1548
{
@@ -1516,8 +1551,7 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
1516
1551
ReleaseBuffer (vacrel -> next_unskippable_vmbuffer );
1517
1552
vacrel -> next_unskippable_vmbuffer = InvalidBuffer ;
1518
1553
}
1519
- * blkno = vacrel -> rel_pages ;
1520
- return false;
1554
+ return InvalidBlockNumber ;
1521
1555
}
1522
1556
1523
1557
/*
@@ -1566,9 +1600,10 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
1566
1600
* but chose not to. We know that they are all-visible in the VM,
1567
1601
* otherwise they would've been unskippable.
1568
1602
*/
1569
- * blkno = vacrel -> current_block = next_block ;
1570
- * blk_info |= VAC_BLK_ALL_VISIBLE_ACCORDING_TO_VM ;
1571
- return true;
1603
+ vacrel -> current_block = next_block ;
1604
+ blk_info |= VAC_BLK_ALL_VISIBLE_ACCORDING_TO_VM ;
1605
+ * ((uint8 * ) per_buffer_data ) = blk_info ;
1606
+ return vacrel -> current_block ;
1572
1607
}
1573
1608
else
1574
1609
{
@@ -1578,12 +1613,13 @@ heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
1578
1613
*/
1579
1614
Assert (next_block == vacrel -> next_unskippable_block );
1580
1615
1581
- * blkno = vacrel -> current_block = next_block ;
1616
+ vacrel -> current_block = next_block ;
1582
1617
if (vacrel -> next_unskippable_allvis )
1583
- * blk_info |= VAC_BLK_ALL_VISIBLE_ACCORDING_TO_VM ;
1618
+ blk_info |= VAC_BLK_ALL_VISIBLE_ACCORDING_TO_VM ;
1584
1619
if (vacrel -> next_unskippable_eager_scanned )
1585
- * blk_info |= VAC_BLK_WAS_EAGER_SCANNED ;
1586
- return true;
1620
+ blk_info |= VAC_BLK_WAS_EAGER_SCANNED ;
1621
+ * ((uint8 * ) per_buffer_data ) = blk_info ;
1622
+ return vacrel -> current_block ;
1587
1623
}
1588
1624
}
1589
1625
0 commit comments