diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index dee1c5b..6b5f96a 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ] +VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING | EMERGENCY } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ] VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table_name ] VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name [ (column_name [, ...] ) ] ] @@ -149,6 +149,16 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ + EMERGENCY + + + Don't extend the visibility map or free space map, it can be helpful in + disk space full cases. + + + + + table_name diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c index b472d31..06bf2fa 100644 --- a/src/backend/access/heap/visibilitymap.c +++ b/src/backend/access/heap/visibilitymap.c @@ -213,6 +213,19 @@ visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf) void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf) { + visibilitymap_pin_ex(rel, heapBlk, buf, true); +} + +/* + * visibilitymap_pin_ex - pin a map page for setting a bit + * + * Same as visibilitymap_pin, additionally extend parameter can be provided + * to allow extend page or not. + */ +void +visibilitymap_pin_ex(Relation rel, BlockNumber heapBlk, Buffer *buf, + bool extend) +{ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); /* Reuse the old pinned buffer if possible */ @@ -223,7 +236,7 @@ visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf) ReleaseBuffer(*buf); } - *buf = vm_readbuf(rel, mapBlock, true); + *buf = vm_readbuf(rel, mapBlock, extend); } /* @@ -286,6 +299,10 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, if (BufferIsValid(heapBuf) && BufferGetBlockNumber(heapBuf) != heapBlk) elog(ERROR, "wrong heap buffer passed to visibilitymap_set"); + /* In case of invalid buffer just return */ + if(vmBuf == InvalidBuffer) + return; + /* Check that we have the right VM page pinned */ if (!BufferIsValid(vmBuf) || BufferGetBlockNumber(vmBuf) != mapBlock) elog(ERROR, "wrong VM buffer passed to visibilitymap_set"); diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 32b6fdd..e86626f 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -140,7 +140,8 @@ static BufferAccessStrategy vac_strategy; static void lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, Relation *Irel, int nindexes, bool aggressive); -static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats); +static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, + int options); static bool lazy_check_needs_freeze(Buffer buf, bool *hastup); static void lazy_vacuum_index(Relation indrel, IndexBulkDeleteResult **stats, @@ -150,7 +151,8 @@ static void lazy_cleanup_index(Relation indrel, LVRelStats *vacrelstats); static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, int tupindex, LVRelStats *vacrelstats, Buffer *vmbuffer); -static bool should_attempt_truncation(LVRelStats *vacrelstats); +static bool should_attempt_truncation(LVRelStats *vacrelstats, + int options); static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats); static BlockNumber count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats); @@ -273,7 +275,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, /* * Optionally truncate the relation. */ - if (should_attempt_truncation(vacrelstats)) + if (should_attempt_truncation(vacrelstats, options)) lazy_truncate_heap(onerel, vacrelstats); /* Report that we are now doing final cleanup */ @@ -593,7 +595,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, /* see note above about forcing scanning of last page */ #define FORCE_CHECK_PAGE() \ - (blkno == nblocks - 1 && should_attempt_truncation(vacrelstats)) + (blkno == nblocks - 1 && should_attempt_truncation(vacrelstats, options)) pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno); @@ -722,7 +724,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, pgstat_progress_update_multi_param(2, hvp_index, hvp_val); /* Remove tuples from heap */ - lazy_vacuum_heap(onerel, vacrelstats); + lazy_vacuum_heap(onerel, vacrelstats, options); /* * Forget the now-vacuumed tuples, and press on, but be careful @@ -746,7 +748,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, * cycle of index vacuuming. * */ - visibilitymap_pin(onerel, blkno, &vmbuffer); + visibilitymap_pin_ex(onerel, blkno, &vmbuffer, + !(options & VACOPT_EMERGENCY)); buf = ReadBufferExtended(onerel, MAIN_FORKNUM, blkno, RBM_NORMAL, vac_strategy); @@ -852,7 +855,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, MarkBufferDirty(buf); UnlockReleaseBuffer(buf); - RecordPageWithFreeSpace(onerel, blkno, freespace); + RecordPageWithFreeSpace_ex(onerel, blkno, freespace, + !(options & VACOPT_EMERGENCY)); continue; } @@ -891,7 +895,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, } UnlockReleaseBuffer(buf); - RecordPageWithFreeSpace(onerel, blkno, freespace); + RecordPageWithFreeSpace_ex(onerel, blkno, freespace, + !(options & VACOPT_EMERGENCY)); continue; } @@ -1236,7 +1241,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, * taken if there are no indexes.) */ if (vacrelstats->num_dead_tuples == prev_dead_count) - RecordPageWithFreeSpace(onerel, blkno, freespace); + RecordPageWithFreeSpace_ex(onerel, blkno, freespace, + !(options & VACOPT_EMERGENCY)); } /* report that everything is scanned and vacuumed */ @@ -1295,7 +1301,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, /* Remove tuples from heap */ pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_VACUUM_HEAP); - lazy_vacuum_heap(onerel, vacrelstats); + lazy_vacuum_heap(onerel, vacrelstats, options); vacrelstats->num_index_scans++; } @@ -1358,7 +1364,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, * process index entry removal in batches as large as possible. */ static void -lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats) +lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, int options) { int tupindex; int npages; @@ -1395,7 +1401,8 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats) freespace = PageGetHeapFreeSpace(page); UnlockReleaseBuffer(buf); - RecordPageWithFreeSpace(onerel, tblk, freespace); + RecordPageWithFreeSpace_ex(onerel, tblk, freespace, + !(options & VACOPT_EMERGENCY)); npages++; } @@ -1666,10 +1673,14 @@ lazy_cleanup_index(Relation indrel, * careful to depend only on fields that lazy_scan_heap updates on-the-fly. */ static bool -should_attempt_truncation(LVRelStats *vacrelstats) +should_attempt_truncation(LVRelStats *vacrelstats, int options) { BlockNumber possibly_freeable; + /* In case of EMERGENCY option always attempt truncate */ + if(options & VACOPT_EMERGENCY) + return true; + possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages; if (possibly_freeable > 0 && (possibly_freeable >= REL_TRUNCATE_MINIMUM || diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index edf4516..2f4343e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9374,6 +9374,8 @@ vacuum_option_elem: { if (strcmp($1, "disable_page_skipping") == 0) $$ = VACOPT_DISABLE_PAGE_SKIPPING; + else if (strcmp($1, "emergency") == 0) + $$ = VACOPT_EMERGENCY; else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index bbd90c9..0f8a4e9 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -107,6 +107,8 @@ static Size fsm_space_cat_to_avail(uint8 cat); /* workhorse functions for various operations */ static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue); +static int fsm_set_and_search_ex(Relation rel, FSMAddress addr, uint16 slot, + uint8 newValue, uint8 minValue, bool extend); static BlockNumber fsm_search(Relation rel, uint8 min_cat); static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof); static BlockNumber fsm_get_lastblckno(Relation rel, FSMAddress addr); @@ -180,6 +182,19 @@ RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage, void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail) { + RecordPageWithFreeSpace_ex(rel, heapBlk, spaceAvail, true); +} + +/* + * RecordPageWithFreeSpace_ex - update info about a page. + * + * Same as RecordPageWithFreeSpace, additionally extend parameter can be + * provided to allow extend page or not. + */ +void +RecordPageWithFreeSpace_ex(Relation rel, BlockNumber heapBlk, Size spaceAvail, + bool extend) +{ int new_cat = fsm_space_avail_to_cat(spaceAvail); FSMAddress addr; uint16 slot; @@ -650,11 +665,27 @@ static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue) { + return fsm_set_and_search_ex(rel, addr, slot, newValue, minValue, true); +} + +/* + * Set value in given FSM page and slot. + * + * Same as fsm_set_and_search, additionally extend parameter can be provided + * to allow extend page or not. + */ +static int +fsm_set_and_search_ex(Relation rel, FSMAddress addr, uint16 slot, + uint8 newValue, uint8 minValue, bool extend) +{ Buffer buf; Page page; int newslot = -1; - buf = fsm_readbuf(rel, addr, true); + buf = fsm_readbuf(rel, addr, extend); + if(buf == InvalidBuffer) + return -1; + LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); page = BufferGetPage(buf); diff --git a/src/include/access/visibilitymap.h b/src/include/access/visibilitymap.h index fca99ca..c1e5aa0 100644 --- a/src/include/access/visibilitymap.h +++ b/src/include/access/visibilitymap.h @@ -38,6 +38,8 @@ extern void visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf); extern void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf); +extern void visibilitymap_pin_ex(Relation rel, BlockNumber heapBlk, + Buffer *vmbuf, bool extend); extern bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf); extern void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d36d9c6..d48bbba 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2823,7 +2823,8 @@ typedef enum VacuumOption VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ - VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ + VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7, /* don't skip any pages */ + VACOPT_EMERGENCY = 1 << 8 /* EMERGENCY option */ } VacuumOption; typedef struct VacuumStmt diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h index 77b3bc3..8bf88fa 100644 --- a/src/include/storage/freespace.h +++ b/src/include/storage/freespace.h @@ -27,6 +27,8 @@ extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel, Size spaceNeeded); extern void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail); +extern void RecordPageWithFreeSpace_ex(Relation rel, BlockNumber heapBlk, + Size spaceAvail, bool extend); extern void XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk, Size spaceAvail); diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index 9b604be..c006117 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -80,5 +80,6 @@ CONTEXT: SQL function "do_analyze" statement 1 SQL function "wrap_do_analyze" statement 1 VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (EMERGENCY) vaccluster; DROP TABLE vaccluster; DROP TABLE vactst; diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index 7b819f6..a50dcab 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -61,6 +61,7 @@ VACUUM FULL vaccluster; VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (EMERGENCY) vaccluster; DROP TABLE vaccluster; DROP TABLE vactst;