Skip to content

Commit 047f205

Browse files
committed
Fix a missed case in code for "moving average" estimate of reltuples.
It is possible for VACUUM to scan no pages at all, if the visibility map shows that all pages are all-visible. In this situation VACUUM has no new information to report about the relation's tuple density, so it wasn't changing pg_class.reltuples ... but it updated pg_class.relpages anyway. That's wrong in general, since there is no evidence to justify changing the density ratio reltuples/relpages, but it's particularly bad if the previous state was relpages=reltuples=0, which means "unknown tuple density". We just replaced "unknown" with "zero". ANALYZE would eventually recover from this, but it could take a lot of repetitions of ANALYZE to do so if the relation size is much larger than the maximum number of pages ANALYZE will scan, because of the moving-average behavior introduced by commit b4b6923. The only known situation where we could have relpages=reltuples=0 and yet the visibility map asserts everything's visible is immediately following a pg_upgrade. It might be advisable for pg_upgrade to try to preserve the relpages/reltuples statistics; but in any case this code is wrong on its own terms, so fix it. Per report from Sergey Koposov. Back-patch to 8.4, where the visibility map was introduced, same as the previous change.
1 parent 2de0fde commit 047f205

File tree

3 files changed

+38
-12
lines changed

3 files changed

+38
-12
lines changed

src/backend/commands/vacuum.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,10 @@ vac_estimate_reltuples(Relation relation, bool is_analyze,
485485
return scanned_tuples;
486486

487487
/*
488-
* If scanned_pages is zero but total_pages isn't, keep the existing
489-
* value of reltuples.
488+
* If scanned_pages is zero but total_pages isn't, keep the existing value
489+
* of reltuples. (Note: callers should avoid updating the pg_class
490+
* statistics in this situation, since no new information has been
491+
* provided.)
490492
*/
491493
if (scanned_pages == 0)
492494
return old_rel_tuples;

src/backend/commands/vacuumlazy.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ typedef struct LVRelStats
8484
/* hasindex = true means two-pass strategy; false means one-pass */
8585
bool hasindex;
8686
/* Overall statistics about rel */
87+
BlockNumber old_rel_pages; /* previous value of pg_class.relpages */
8788
BlockNumber rel_pages; /* total number of pages */
8889
BlockNumber scanned_pages; /* number of pages we examined */
8990
double scanned_tuples; /* counts only tuples on scanned pages */
@@ -154,6 +155,9 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
154155
TimestampTz starttime = 0;
155156
bool scan_all;
156157
TransactionId freezeTableLimit;
158+
BlockNumber new_rel_pages;
159+
double new_rel_tuples;
160+
TransactionId new_frozen_xid;
157161

158162
pg_rusage_init(&ru0);
159163

@@ -176,6 +180,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
176180

177181
vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
178182

183+
vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
179184
vacrelstats->old_rel_tuples = onerel->rd_rel->reltuples;
180185
vacrelstats->num_index_scans = 0;
181186

@@ -205,20 +210,39 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
205210
FreeSpaceMapVacuum(onerel);
206211

207212
/*
208-
* Update statistics in pg_class. But don't change relfrozenxid if we
209-
* skipped any pages.
213+
* Update statistics in pg_class.
214+
*
215+
* A corner case here is that if we scanned no pages at all because every
216+
* page is all-visible, we should not update relpages/reltuples, because
217+
* we have no new information to contribute. In particular this keeps
218+
* us from replacing relpages=reltuples=0 (which means "unknown tuple
219+
* density") with nonzero relpages and reltuples=0 (which means "zero
220+
* tuple density") unless there's some actual evidence for the latter.
221+
*
222+
* Also, don't change relfrozenxid if we skipped any pages, since then
223+
* we don't know for certain that all tuples have a newer xmin.
210224
*/
225+
new_rel_pages = vacrelstats->rel_pages;
226+
new_rel_tuples = vacrelstats->new_rel_tuples;
227+
if (vacrelstats->scanned_pages == 0 && new_rel_pages > 0)
228+
{
229+
new_rel_pages = vacrelstats->old_rel_pages;
230+
new_rel_tuples = vacrelstats->old_rel_tuples;
231+
}
232+
233+
new_frozen_xid = FreezeLimit;
234+
if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
235+
new_frozen_xid = InvalidTransactionId;
236+
211237
vac_update_relstats(onerel,
212-
vacrelstats->rel_pages, vacrelstats->new_rel_tuples,
238+
new_rel_pages, new_rel_tuples,
213239
vacrelstats->hasindex,
214-
(vacrelstats->scanned_pages < vacrelstats->rel_pages) ?
215-
InvalidTransactionId :
216-
FreezeLimit);
240+
new_frozen_xid);
217241

218242
/* report results to the stats collector, too */
219243
pgstat_report_vacuum(RelationGetRelid(onerel),
220244
onerel->rd_rel->relisshared,
221-
vacrelstats->new_rel_tuples);
245+
new_rel_tuples);
222246

223247
/* and log the action if appropriate */
224248
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
@@ -238,7 +262,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
238262
vacrelstats->pages_removed,
239263
vacrelstats->rel_pages,
240264
vacrelstats->tuples_deleted,
241-
vacrelstats->new_rel_tuples,
265+
new_rel_tuples,
242266
pg_rusage_show(&ru0))));
243267
}
244268
}

src/backend/utils/cache/relcache.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,8 @@ formrdesc(const char *relationName, Oid relationReltype,
14541454
*/
14551455
relation->rd_rel->relistemp = false;
14561456

1457-
relation->rd_rel->relpages = 1;
1458-
relation->rd_rel->reltuples = 1;
1457+
relation->rd_rel->relpages = 0;
1458+
relation->rd_rel->reltuples = 0;
14591459
relation->rd_rel->relkind = RELKIND_RELATION;
14601460
relation->rd_rel->relhasoids = hasoids;
14611461
relation->rd_rel->relnatts = (int16) natts;

0 commit comments

Comments
 (0)