Re: Spurious "apparent wraparound" via SimpleLruTruncate() rounding
Noah Misch <noah@leadboat.com>
From: Noah Misch <noah@leadboat.com>
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Cc: pgsql-hackers@postgresql.org
Date: 2020-01-05T01:19:16Z
Lists: pgsql-hackers
On Sun, Jan 05, 2020 at 01:33:55AM +0100, Tomas Vondra wrote: > On Wed, Feb 13, 2019 at 11:26:23PM -0800, Noah Misch wrote: > >a. Fix the rounding in SimpleLruTruncate(). (The patch I posted upthread is > > wrong; I will correct it in a separate message.) > > > >b. Arrange so only one backend runs vac_truncate_clog() at a time. Other than > > AsyncCtl, every SLRU truncation appears in vac_truncate_clog(), in a > > checkpoint, or in the startup process. Hence, also arrange for only one > > backend to call SimpleLruTruncate(AsyncCtl) at a time. > > > >c. Test "apparent wraparound" before writing WAL, and don't WAL-log > > truncations that "apparent wraparound" forces us to skip. > > > >d. Hold the ControlLock for the entirety of SimpleLruTruncate(). This removes > > the TOCTOU race condition, but TransactionIdDidCommit() and other key > > operations would be waiting behind filesystem I/O. > >With both (a) and (b), the only way I'd know to reach the "apparent > >wraparound" message is to restart in single-user mode and burn XIDs to the > >point of bona fide wraparound. Hence, I propose to back-patch (a) and (b), > >and I propose (c) for HEAD only. I don't want (d), which threatens > >performance too much. I would rather not have (e), because I expect it's more > >complex than (b) and fixes strictly less than (b) fixes. > Seems reasonable, although I wonder how much more expensive would just > doing (d) be. It seems by far the least complex solution, and it moves > "just" the SlruScanDirectory() call before the lock. It's true it adds > I/O requests, OTOH it's just unlink() without fsync() and I'd expect the > number of files to be relatively low. Plus we already do SimpleLruWaitIO > and SlruInternalWritePage in the loop. Trivial read-only transactions often need CLogControlLock to check tuple visibility. If an unlink() takes 1s, stalling read-only transactions for that 1s is a big problem. SimpleLruWaitIO() and SlruInternalWritePage() release the control lock during I/O, then re-acquire it. (Moreover, SimpleLruTruncate() rarely calls them. Calling them implies a page old enough to truncate was modified after the most recent checkpoint.) > BTW isn't that an issue that SlruInternalDeleteSegment does not do any > fsync calls after unlinking the segments? If the system crashes/reboots > before this becomes persistent (i.e. some of the segments reappear, > won't that cause a problem)? I think not; we could turn SlruInternalDeleteSegment() into a no-op, and the only SQL-visible consequence would be extra disk usage. CheckPoint fields tell the server what region of slru data is meaningful, and segments outside that range merely waste disk space. (If that's not true and can't be made true, we'd also need to stop ignoring the unlink() return value.) > It's a bit unfortunate that a patch for a data corruption / loss issue > (even if a low-probability one) fell through multiple commitfests. Thanks for investing in steps to fix that.
Commits
-
Prevent excess SimpleLruTruncate() deletion.
- c424c75f5382 9.5.25 landed
- 9afba9b0e282 10.16 landed
- 677f6cb1d987 11.11 landed
- 66087f7e922c 12.6 landed
- 1a31d8c52db4 9.6.21 landed
- 6eb3fc7fcd89 13.2 landed
- 6db992833c04 14.0 landed
-
Fix unlinking of SLRU segments.
- c732c3f8c122 14.0 cited
-
Defer flushing of SLRU files.
- dee663f78439 14.0 cited
-
Change XID and mxact limits to warn at 40M and stop at 3M.
- cd5e82256de5 14.0 cited