Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ede62e5

Browse files
committed
Add VACUUM (DISABLE_PAGE_SKIPPING) for emergencies.
If you really want to vacuum every single page in the relation, regardless of apparent visibility status or anything else, you can use this option. In previous releases, this behavior could be achieved using VACUUM (FREEZE), but because we can now recognize all-frozen pages as not needing to be frozen again, that no longer works. There should be no need for routine use of this option, but maybe bugs or disaster recovery will necessitate its use. Patch by me, reviewed by Andres Freund.
1 parent 20eb273 commit ede62e5

File tree

7 files changed

+97
-46
lines changed

7 files changed

+97
-46
lines changed

doc/src/sgml/ref/vacuum.sgml

+20-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
24+
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
2525
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> ]
2626
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
2727
</synopsis>
@@ -129,6 +129,25 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
129129
</listitem>
130130
</varlistentry>
131131

132+
<varlistentry>
133+
<term><literal>DISABLE_PAGE_SKIPPING</literal></term>
134+
<listitem>
135+
<para>
136+
Normally, <command>VACUUM</> will skip pages based on the <link
137+
linkend="vacuum-for-visibility-map">visibility map</>. Pages where
138+
all tuples are known to be frozen can always be skipped, and those
139+
where all tuples are known to be visible to all transactions may be
140+
skipped except when performing an aggressive vacuum. Furthermore,
141+
except when performing an aggressive vacuum, some pages may be skipped
142+
in order to avoid waiting for other sessions to finish using them.
143+
This option disables all page-skipping behavior, and is intended to
144+
be used only the contents of the visibility map are thought to
145+
be suspect, which should happen only if there is a hardware or software
146+
issue causing database corruption.
147+
</para>
148+
</listitem>
149+
</varlistentry>
150+
132151
<varlistentry>
133152
<term><replaceable class="PARAMETER">table_name</replaceable></term>
134153
<listitem>

src/backend/commands/vacuum.c

+9
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
185185
errmsg("%s cannot be executed from VACUUM or ANALYZE",
186186
stmttype)));
187187

188+
/*
189+
* Sanity check DISABLE_PAGE_SKIPPING option.
190+
*/
191+
if ((options & VACOPT_FULL) != 0 &&
192+
(options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
193+
ereport(ERROR,
194+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
195+
errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
196+
188197
/*
189198
* Send info about dead objects to the statistics collector, unless we are
190199
* in autovacuum --- autovacuum.c does this for itself.

src/backend/commands/vacuumlazy.c

+53-44
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ static BufferAccessStrategy vac_strategy;
137137

138138

139139
/* non-export function prototypes */
140-
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
141-
Relation *Irel, int nindexes, bool aggressive);
140+
static void lazy_scan_heap(Relation onerel, int options,
141+
LVRelStats *vacrelstats, Relation *Irel, int nindexes,
142+
bool aggressive);
142143
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
143144
static bool lazy_check_needs_freeze(Buffer buf, bool *hastup);
144145
static void lazy_vacuum_index(Relation indrel,
@@ -223,15 +224,17 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
223224
&MultiXactCutoff, &mxactFullScanLimit);
224225

225226
/*
226-
* We request an aggressive scan if either the table's frozen Xid is now
227-
* older than or equal to the requested Xid full-table scan limit; or if
228-
* the table's minimum MultiXactId is older than or equal to the requested
229-
* mxid full-table scan limit.
227+
* We request an aggressive scan if the table's frozen Xid is now older
228+
* than or equal to the requested Xid full-table scan limit; or if the
229+
* table's minimum MultiXactId is older than or equal to the requested
230+
* mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified.
230231
*/
231232
aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
232233
xidFullScanLimit);
233234
aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
234235
mxactFullScanLimit);
236+
if (options & VACOPT_DISABLE_PAGE_SKIPPING)
237+
aggressive = true;
235238

236239
vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
237240

@@ -246,7 +249,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
246249
vacrelstats->hasindex = (nindexes > 0);
247250

248251
/* Do the vacuuming */
249-
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, aggressive);
252+
lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive);
250253

251254
/* Done with indexes */
252255
vac_close_indexes(nindexes, Irel, NoLock);
@@ -441,7 +444,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
441444
* reference them have been killed.
442445
*/
443446
static void
444-
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
447+
lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
445448
Relation *Irel, int nindexes, bool aggressive)
446449
{
447450
BlockNumber nblocks,
@@ -542,25 +545,28 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
542545
* the last page. This is worth avoiding mainly because such a lock must
543546
* be replayed on any hot standby, where it can be disruptive.
544547
*/
545-
for (next_unskippable_block = 0;
546-
next_unskippable_block < nblocks;
547-
next_unskippable_block++)
548+
next_unskippable_block = 0;
549+
if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
548550
{
549-
uint8 vmstatus;
550-
551-
vmstatus = visibilitymap_get_status(onerel, next_unskippable_block,
552-
&vmbuffer);
553-
if (aggressive)
551+
while (next_unskippable_block < nblocks)
554552
{
555-
if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0)
556-
break;
557-
}
558-
else
559-
{
560-
if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0)
561-
break;
553+
uint8 vmstatus;
554+
555+
vmstatus = visibilitymap_get_status(onerel, next_unskippable_block,
556+
&vmbuffer);
557+
if (aggressive)
558+
{
559+
if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0)
560+
break;
561+
}
562+
else
563+
{
564+
if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0)
565+
break;
566+
}
567+
vacuum_delay_point();
568+
next_unskippable_block++;
562569
}
563-
vacuum_delay_point();
564570
}
565571

566572
if (next_unskippable_block >= SKIP_PAGES_THRESHOLD)
@@ -594,26 +600,29 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
594600
if (blkno == next_unskippable_block)
595601
{
596602
/* Time to advance next_unskippable_block */
597-
for (next_unskippable_block++;
598-
next_unskippable_block < nblocks;
599-
next_unskippable_block++)
603+
next_unskippable_block++;
604+
if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
600605
{
601-
uint8 vmskipflags;
602-
603-
vmskipflags = visibilitymap_get_status(onerel,
604-
next_unskippable_block,
605-
&vmbuffer);
606-
if (aggressive)
606+
while (next_unskippable_block < nblocks)
607607
{
608-
if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0)
609-
break;
610-
}
611-
else
612-
{
613-
if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0)
614-
break;
608+
uint8 vmskipflags;
609+
610+
vmskipflags = visibilitymap_get_status(onerel,
611+
next_unskippable_block,
612+
&vmbuffer);
613+
if (aggressive)
614+
{
615+
if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0)
616+
break;
617+
}
618+
else
619+
{
620+
if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0)
621+
break;
622+
}
623+
vacuum_delay_point();
624+
next_unskippable_block++;
615625
}
616-
vacuum_delay_point();
617626
}
618627

619628
/*
@@ -1054,7 +1063,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
10541063
}
10551064
else
10561065
{
1057-
bool tuple_totally_frozen;
1066+
bool tuple_totally_frozen;
10581067

10591068
num_tuples += 1;
10601069
hastup = true;
@@ -1064,8 +1073,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
10641073
* freezing. Note we already have exclusive buffer lock.
10651074
*/
10661075
if (heap_prepare_freeze_tuple(tuple.t_data, FreezeLimit,
1067-
MultiXactCutoff, &frozen[nfrozen],
1068-
&tuple_totally_frozen))
1076+
MultiXactCutoff, &frozen[nfrozen],
1077+
&tuple_totally_frozen))
10691078
frozen[nfrozen++].offset = offnum;
10701079

10711080
if (!tuple_totally_frozen)

src/backend/parser/gram.y

+10
Original file line numberDiff line numberDiff line change
@@ -9370,6 +9370,16 @@ vacuum_option_elem:
93709370
| VERBOSE { $$ = VACOPT_VERBOSE; }
93719371
| FREEZE { $$ = VACOPT_FREEZE; }
93729372
| FULL { $$ = VACOPT_FULL; }
9373+
| IDENT
9374+
{
9375+
if (strcmp($1, "disable_page_skipping") == 0)
9376+
$$ = VACOPT_DISABLE_PAGE_SKIPPING;
9377+
else
9378+
ereport(ERROR,
9379+
(errcode(ERRCODE_SYNTAX_ERROR),
9380+
errmsg("unrecognized VACUUM option \"%s\"", $1),
9381+
parser_errposition(@1)));
9382+
}
93739383
;
93749384

93759385
AnalyzeStmt:

src/include/nodes/parsenodes.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2822,7 +2822,8 @@ typedef enum VacuumOption
28222822
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
28232823
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
28242824
VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */
2825-
VACOPT_SKIPTOAST = 1 << 6 /* don't process the TOAST table, if any */
2825+
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
2826+
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
28262827
} VacuumOption;
28272828

28282829
typedef struct VacuumStmt

src/test/regress/expected/vacuum.out

+1
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,6 @@ ERROR: ANALYZE cannot be executed from VACUUM or ANALYZE
7979
CONTEXT: SQL function "do_analyze" statement 1
8080
SQL function "wrap_do_analyze" statement 1
8181
VACUUM FULL vactst;
82+
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
8283
DROP TABLE vaccluster;
8384
DROP TABLE vactst;

src/test/regress/sql/vacuum.sql

+2
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,7 @@ VACUUM FULL pg_database;
6060
VACUUM FULL vaccluster;
6161
VACUUM FULL vactst;
6262

63+
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
64+
6365
DROP TABLE vaccluster;
6466
DROP TABLE vactst;

0 commit comments

Comments
 (0)