*** a/src/backend/commands/vacuumlazy.c --- b/src/backend/commands/vacuumlazy.c *************** *** 113,118 **** typedef struct LVRelStats --- 113,119 ---- BlockNumber pages_removed; double tuples_deleted; BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */ + BlockNumber skipped_pages; /* actually, last skipped page + 1 */ /* List of TIDs of tuples we intend to delete */ /* NB: this list is ordered by TID address */ int num_dead_tuples; /* current # of entries */ *************** *** 475,480 **** lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, --- 476,482 ---- vacrelstats->rel_pages = nblocks; vacrelstats->scanned_pages = 0; vacrelstats->nonempty_pages = 0; + vacrelstats->skipped_pages = 0; vacrelstats->latestRemovedXid = InvalidTransactionId; lazy_space_alloc(vacrelstats, nblocks); *************** *** 568,574 **** lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, --- 570,579 ---- { /* Current block is all-visible */ if (skipping_all_visible_blocks && !scan_all) + { + vacrelstats->skipped_pages = blkno + 1; continue; + } all_visible_according_to_vm = true; } *************** *** 1559,1568 **** static BlockNumber --- 1564,1583 ---- count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) { BlockNumber blkno; + BlockNumber trust_vm; instr_time starttime; + Buffer vmbuffer = InvalidBuffer; /* Initialize the starttime if we check for conflicting lock requests */ INSTR_TIME_SET_CURRENT(starttime); + + /* + * Pages that were inspected and found to be empty by this vacuum run + * must still be empty if the vm bit is still set. Only vacuum sets + * vm bits, and only one vacuum can exist in a table at one time. + */ + trust_vm = vacrelstats->nonempty_pages > vacrelstats->skipped_pages ? + vacrelstats->nonempty_pages : vacrelstats->skipped_pages; /* Strange coding of loop control is needed because blkno is unsigned */ blkno = vacrelstats->rel_pages; *************** *** 1600,1605 **** count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) --- 1615,1622 ---- RelationGetRelationName(onerel)))); vacrelstats->lock_waiter_detected = true; + if (BufferIsValid(vmbuffer)) + ReleaseBuffer(vmbuffer); return blkno; } starttime = currenttime; *************** *** 1614,1620 **** count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) CHECK_FOR_INTERRUPTS(); blkno--; ! buf = ReadBufferExtended(onerel, MAIN_FORKNUM, blkno, RBM_NORMAL, vac_strategy); --- 1631,1644 ---- CHECK_FOR_INTERRUPTS(); blkno--; ! ! /* ! * Verify the page is empty or not based on visibility map test, instead ! * of verifying from the page contents. ! */ ! if ((blkno >= trust_vm) && visibilitymap_test(onerel, blkno, &vmbuffer)) ! continue; ! buf = ReadBufferExtended(onerel, MAIN_FORKNUM, blkno, RBM_NORMAL, vac_strategy); *************** *** 1657,1663 **** count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) --- 1681,1691 ---- /* Done scanning if we found a tuple here */ if (hastup) + { + if (BufferIsValid(vmbuffer)) + ReleaseBuffer(vmbuffer); return blkno + 1; + } } /* *************** *** 1665,1670 **** count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) --- 1693,1700 ---- * pages still are; we need not bother to look at the last known-nonempty * page. */ + if (BufferIsValid(vmbuffer)) + ReleaseBuffer(vmbuffer); return vacrelstats->nonempty_pages; }