From 6cb766385fa27c7de6e9419a7eee13994d47edf5 Mon Sep 17 00:00:00 2001 From: Claudio Freire Date: Mon, 26 Mar 2018 13:12:22 -0300 Subject: [PATCH 2/4] Vacuum: do a partial FSM vacuum at the beginning A partial FSM vacuum right at the start of the vacuum process helps ameliorate issues with cancelled autovacuums never updating the FSM. --- src/backend/access/brin/brin.c | 2 +- src/backend/access/brin/brin_pageops.c | 10 +++++----- src/backend/commands/vacuumlazy.c | 25 ++++++++++++++++++++++++- src/backend/storage/freespace/freespace.c | 26 ++++++++++++++++++++------ src/backend/storage/freespace/indexfsm.c | 2 +- src/include/storage/freespace.h | 2 +- 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 0e5849e..292bd11 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1471,5 +1471,5 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) * the way to the top. */ if (vacuum_fsm) - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); } diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c index 60a7025..d4c7a87 100644 --- a/src/backend/access/brin/brin_pageops.c +++ b/src/backend/access/brin/brin_pageops.c @@ -136,7 +136,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, brin_initialize_empty_new_buffer(idxrel, newbuf); UnlockReleaseBuffer(newbuf); if (extended) - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); } return false; } @@ -156,7 +156,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, brin_initialize_empty_new_buffer(idxrel, newbuf); UnlockReleaseBuffer(newbuf); if (extended) - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); } return false; } @@ -211,7 +211,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK); if (extended) - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); return true; } @@ -313,7 +313,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, { Assert(BlockNumberIsValid(newblk)); RecordPageWithFreeSpace(idxrel, newblk, freespace); - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); } return true; @@ -457,7 +457,7 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange, LockBuffer(revmapbuf, BUFFER_LOCK_UNLOCK); if (extended) - FreeSpaceMapVacuum(idxrel); + FreeSpaceMapVacuum(idxrel, 0); return off; } diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index c44996f..ff807a3 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -116,6 +116,18 @@ */ #define PREFETCH_SIZE ((BlockNumber) 32) +/* + * If autovacuums get regularly cancelled, the FSM may never be + * vacuumed. To work around that, we perform an initial partial + * FSM vacuum at the beginning of the vacuum process, to quickly + * make existing unmarked free space visible. To avoid useless + * redundant work, however, we avoid recursing into branches + * that already have a set amount of free space, we only try + * to discover unmarked free space. This value controls how + * much free space is enough free space in that context. + */ +#define INITIAL_PARTIAL_FSM_VACUUM_THRESHOLD ((Size) BLCKSZ/4) + typedef struct LVRelStats { /* hasindex = true means two-pass strategy; false means one-pass */ @@ -262,6 +274,17 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, vacrelstats->pages_removed = 0; vacrelstats->lock_waiter_detected = false; + /* + * Vacuum the Free Space Map partially before we start. + * If an earlier vacuum was canceled, and that's likely in + * highly contended tables, we may need to finish up. If we do + * it now, we make the space visible to other backends regardless + * of whether we succeed in finishing this time around. + * Don't bother checking branches that already have usable space, + * though. + */ + FreeSpaceMapVacuum(onerel, INITIAL_PARTIAL_FSM_VACUUM_THRESHOLD); + /* Open all indexes of the relation */ vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel); vacrelstats->hasindex = (nindexes > 0); @@ -299,7 +322,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP); /* Vacuum the Free Space Map */ - FreeSpaceMapVacuum(onerel); + FreeSpaceMapVacuum(onerel, 0); /* * Update statistics in pg_class. diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index 6fce3ab..2f48092 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -108,7 +108,7 @@ static Size fsm_space_cat_to_avail(uint8 cat); static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue); static BlockNumber fsm_search(Relation rel, uint8 min_cat); -static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof, +static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, uint8 threshold, bool *eof, BlockNumber start, BlockNumber end); static BlockNumber fsm_get_lastblckno(Relation rel, FSMAddress addr); static void fsm_update_recursive(Relation rel, FSMAddress addr, uint8 new_cat); @@ -375,9 +375,15 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks) /* * FreeSpaceMapVacuum - scan and fix any inconsistencies in the FSM + * + * If threshold is nonzero, a partial scan is made, skipping branches + * that already contain that much free space. Such a can be faster + * if many branches can be skipped, but it can't guarantee all + * inconsistencies will be fixed, only those under branches with less + * free space recorded than the threshold. */ void -FreeSpaceMapVacuum(Relation rel) +FreeSpaceMapVacuum(Relation rel, Size threshold) { bool dummy; @@ -385,7 +391,7 @@ FreeSpaceMapVacuum(Relation rel) * Traverse the tree in depth-first order. The tree is stored physically * in depth-first order, so this should be pretty I/O efficient. */ - fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, &dummy, + fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, fsm_space_avail_to_cat(threshold), &dummy, (BlockNumber) 0, MaxBlockNumber); } @@ -403,7 +409,7 @@ FreeSpaceMapVacuumRange(Relation rel, BlockNumber start, BlockNumber end) * Traverse the tree in depth-first order. The tree is stored physically * in depth-first order, so this should be pretty I/O efficient. */ - fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, &dummy, start, end); + fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, 0, &dummy, start, end); } /******** Internal routines ********/ @@ -807,7 +813,7 @@ fsm_search(Relation rel, uint8 min_cat) * that already contain that much free space recorded. */ static uint8 -fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p, +fsm_vacuum_page(Relation rel, FSMAddress addr, uint8 threshold, bool *eof_p, BlockNumber start, BlockNumber end) { Buffer buf; @@ -870,11 +876,19 @@ fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p, { int child_avail; + /* Tree pruning for partial vacuums */ + if (threshold) + { + child_avail = fsm_get_avail(page, slot); + if (child_avail >= threshold) + continue; + } + CHECK_FOR_INTERRUPTS(); /* After we hit end-of-file, just clear the rest of the slots */ if (!eof) - child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot), &eof, + child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot), threshold, &eof, start, end); else child_avail = 0; diff --git a/src/backend/storage/freespace/indexfsm.c b/src/backend/storage/freespace/indexfsm.c index e21047b..dd77a16 100644 --- a/src/backend/storage/freespace/indexfsm.c +++ b/src/backend/storage/freespace/indexfsm.c @@ -70,5 +70,5 @@ RecordUsedIndexPage(Relation rel, BlockNumber usedBlock) void IndexFreeSpaceMapVacuum(Relation rel) { - FreeSpaceMapVacuum(rel); + FreeSpaceMapVacuum(rel, 0); } diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h index 13f6380..f9fe2e4 100644 --- a/src/include/storage/freespace.h +++ b/src/include/storage/freespace.h @@ -31,7 +31,7 @@ extern void XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk, Size spaceAvail); extern void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks); -extern void FreeSpaceMapVacuum(Relation rel); +extern void FreeSpaceMapVacuum(Relation rel, Size threshold); extern void FreeSpaceMapVacuumRange(Relation rel, BlockNumber start, BlockNumber end); extern void UpdateFreeSpaceMap(Relation rel, -- 1.8.4.5