Re: Removing more vacuumlazy.c special cases, relfrozenxid optimizations
Peter Geoghegan <pg@bowt.ie>
On Thu, Jan 6, 2022 at 12:54 PM Robert Haas <robertmhaas@gmail.com> wrote: > On Fri, Dec 17, 2021 at 9:30 PM Peter Geoghegan <pg@bowt.ie> wrote: > > Can we fully get rid of vacuum_freeze_table_age? Maybe even get rid of > > vacuum_freeze_min_age, too? Freezing tuples is a maintenance task for > > physical blocks, but we use logical units (XIDs). > > I don't see how we can get rid of these. We know that catastrophe will > ensue if we fail to freeze old XIDs for a sufficiently long time --- > where sufficiently long has to do with the number of XIDs that have > been subsequently consumed. I don't really disagree with anything you've said, I think. There are a few subtleties here. I'll try to tease them apart. I agree that we cannot do without something like vacrel->FreezeLimit for the foreseeable future -- but the closely related GUC (vacuum_freeze_min_age) is another matter. Although everything you've said in favor of the GUC seems true, the GUC is not a particularly effective (or natural) way of constraining the problem. It just doesn't make sense as a tunable. One obvious reason for this is that the opportunistic freezing stuff is expected to be the thing that usually forces freezing -- not vacuum_freeze_min_age, nor FreezeLimit, nor any other XID-based cutoff. As you more or less pointed out yourself, we still need FreezeLimit as a backstop mechanism. But the value of FreezeLimit can just come from autovacuum_freeze_max_age/2 in all cases (no separate GUC), or something along those lines. We don't particularly expect the value of FreezeLimit to matter, at least most of the time. It should only noticeably affect our behavior during anti-wraparound VACUUMs, which become rare with the patch (e.g. my pgbench_accounts example upthread). Most individual tables will never get even one anti-wraparound VACUUM -- it just doesn't ever come for most tables in practice. My big issue with vacuum_freeze_min_age is that it doesn't really work with the freeze map work in 9.6, which creates problems that I'm trying to address by freezing early and so on. After all, HEAD (and all stable branches) can easily set a page to all-visible (but not all-frozen) in the VM, meaning that the page's tuples won't be considered for freezing until the next aggressive VACUUM. This means that vacuum_freeze_min_age is already frequently ignored by the implementation -- it's conditioned on other things that are practically impossible to predict. Curious about your thoughts on this existing issue with vacuum_freeze_min_age. I am concerned about the "freezing cliff" that it creates. > So it's natural to decide whether or not > we're going to wait for cleanup locks on pages on the basis of how old > the XIDs they contain actually are. I agree, but again, it's only a backstop. With the patch we'd have to be rather unlucky to ever need to wait like this. What are the chances that we keep failing to freeze an old XID from one particular page, again and again? My testing indicates that it's a negligible concern in practice (barring pathological cases with idle cursors, etc). > I think vacuum_freeze_min_age also serves a useful purpose: it > prevents us from freezing data that's going to be modified again or > even deleted in the near future. Since we can't know the future, we > must base our decision on the assumption that the future will be like > the past: if the page hasn't been modified for a while, then we should > assume it's not likely to be modified again soon; otherwise not. But the "freeze early" heuristics work a bit like that anyway. We won't freeze all the tuples on a whole heap page early if we won't otherwise set the heap page to all-visible (not all-frozen) in the VM anyway. > If we > knew the time at which the page had last been modified, it would be > very reasonable to use that here - say, freeze the XIDs if the page > hasn't been touched in an hour, or whatever. But since we lack such > timestamps the XID age is the closest proxy we have. XID age is a *terrible* proxy. The age of an XID in a tuple header may advance quickly, even when nobody modifies the same table at all. I concede that it is true that we are (in some sense) "gambling" by freezing early -- we may end up freezing a tuple that we subsequently update anyway. But aren't we also "gambling" by *not* freezing early? By not freezing, we risk getting into "freezing debt" that will have to be paid off in one ruinously large installment. I would much rather "gamble" on something where we can tolerate consistently "losing" than gamble on something where I cannot ever afford to lose (even if it's much less likely that I'll lose during any given VACUUM operation). Besides all this, I think that we have a rather decent chance of coming out ahead in practice by freezing early. In practice the marginal cost of freezing early is consistently pretty low. Cost-control-driven (as opposed to need-driven) freezing is *supposed* to be cheaper, of course. And like it or not, freezing is really just part of the cost of storing data using Postgres (for the time being, at least). > > The > > risk mostly comes from how much total work we still need to do to > > advance relfrozenxid. If the single old XID is quite old indeed (~1.5 > > billion XIDs), but there is only one, then we just have to freeze one > > tuple to be able to safely advance relfrozenxid (maybe advance it by a > > huge amount!). How long can it take to freeze one tuple, with the > > freeze map, etc? > > I don't really see any reason for optimism here. > IOW, the time that it takes to freeze that one tuple *in theory* might > be small. But in practice it may be very large, because we won't > necessarily get around to it on any meaningful time frame. On second thought I agree that my specific example of 1.5 billion XIDs was a little too optimistic of me. But 50 million XIDs (i.e. the vacuum_freeze_min_age default) is too pessimistic. The important point is that FreezeLimit could plausibly become nothing more than a backstop mechanism, with the design from the patch series -- something that typically has no effect on what tuples actually get frozen. -- Peter Geoghegan
Commits
-
Have VACUUM warn on relfrozenxid "in the future".
- e83ebfe6d767 15.0 landed
-
vacuumlazy.c: Further consolidate resource allocation.
- c42a6fc41dc2 15.0 landed
-
Generalize how VACUUM skips all-frozen pages.
- f3c15cbe5065 15.0 landed
-
Set relfrozenxid to oldest extant XID seen by VACUUM.
- 0b018fabaaba 15.0 landed
-
Doc: Add relfrozenxid Tip to XID wraparound section.
- 05023a237c05 15.0 landed
-
vacuumlazy.c: document vistest and OldestXmin.
- 73f6ec3d3c8d 15.0 cited
-
Increase hash_mem_multiplier default to 2.0.
- 8f388f6f554b 15.0 cited
-
Consolidate VACUUM xid cutoff logic.
- efa4a9462a07 15.0 landed
-
Add VACUUM instrumentation for scanned pages, relfrozenxid.
- 872770fd6ccf 15.0 landed
-
Simplify lazy_scan_heap's handling of scanned pages.
- 44fa84881fff 15.0 landed
-
Try to stabilize reloptions test, again.
- b700f96cffd9 15.0 cited
-
Unify VACUUM VERBOSE and autovacuum logging.
- 49c9d9fcfa9a 15.0 cited
-
Fix possible HOT corruption when RECENTLY_DEAD changes to DEAD while pruning.
- 18b87b201f73 15.0 cited
-
pg_resetxlog: add option to set oldest xid & use by pg_upgrade
- 74cf7d46a91d 15.0 cited
-
Teach VACUUM to bypass unnecessary index vacuuming.
- 5100010ee4d5 14.0 cited
-
Centralize horizon determination for temp tables, fixing bug due to skew.
- 94bc27b57680 14.0 cited
-
pg_surgery: Try to stabilize regression tests.
- 0811f766fd74 14.0 cited
-
Add "split after new tuple" nbtree optimization.
- f21668f328c8 12.0 cited
-
Fix bugs in vacuum of shared rels, by keeping their relcache entries current.
- a54e1f158779 11.0 cited
-
Avoid useless truncation attempts during VACUUM.
- e842908233bb 9.6.0 cited
-
Only skip pages marked as clean in the visibility map, if the last 32
- bf136cf6e376 8.4.0 cited
-
Fix recently-understood problems with handling of XID freezing, particularly
- 48188e1621bb 8.2.0 cited