Re: BUG #17821: Assertion failed in heap_update() due to heap pruning
Noah Misch <noah@leadboat.com>
From: Noah Misch <noah@leadboat.com>
To: Alexander Lakhin <exclusion@gmail.com>
Cc: Melanie Plageman <melanieplageman@gmail.com>, Heikki Linnakangas <hlinnaka@iki.fi>, Andres Freund <andres@anarazel.de>, pgsql-bugs@lists.postgresql.org
Date: 2025-01-11T21:44:54Z
Lists: pgsql-bugs
Attachments
- repro-ItemIdIsNormal-v2.patch (text/plain) patch v2
On Tue, Nov 12, 2024 at 04:26:57PM -0800, Noah Misch wrote: > This looked a lot like the former LP_UNUSED race conditions that inplace.spec > demonstrated. I forked inplace.spec to use regular MVCC updates (attached). > That reproduces the symptoms you describe. This happens because syscache is a > way to get a tuple without a pin on the tuple's page and without satisfying > any snapshot. Either a pin or a snapshot would stop the pruning. That's how > regular UPDATE statements avoid this. > > Some options for fixing it: > > 1. Make the ItemIdIsNormal() check a runtime check that causes heap_update() > to return TM_Deleted. It could Assert(RelationSupportsSysCache(rel)), too. > > 2. Like the previous, but introduce a TM_Pruned. If pruning hadn't happened, > heap_update() would have returned TM_Updated or TM_Deleted. We don't know > which one heap_update() would have returned, because pruning erased the > t_ctid that would have driven that decision. > > 3. Before using a syscache tuple for a heap_update(), pin its buffer and > compare the syscache xmin to the buffer tuple xmin. Normally, they'll > match. If they don't match, ERROR or re-fetch the latest tuple as though > the syscache lookup had experienced a cache miss. > > 4. Take a self-exclusive heavyweight lock before we use the syscache to fetch > the old tuple for a heap_update(). That's how we avoid this if the only > catalog-modifying commands are ALTER TABLE. > > I think this defect can't corrupt catalogs or other data. It's merely a > source of transient failure. I got that wrong ... > While one of the test permutations does update a > pg_class tuple of a different rel, the update then fails with a primary key > violation. All misdirected updates will fail that way, because we don't > update the primary key columns of syscache relations. With assertions > disabled, I think heap_update() looks for an old tuple at lp_off=0, hence > treating the page header as a tuple header. It does. > Bytes of pd_lsn become the > putative xmin. The attached test gets "ERROR: attempted to update invisible > tuple" error from that, but other PageHeader bit patterns might reach "could > not access status of transaction", unique constraint violations, etc. A UNIQUE constraint violation is too late to avoid page header corruption. I modified the test case to reach page header corruption. Attached. Key parts: lsn |checksum|flags|lower|upper|special|pagesize|version|prune_xid ------+--------+-----+-----+-----+-------+--------+-------+--------- 1/6AF8| 0| 0| 0| 11| 38| 8192| 4| 0 (1 row) ERROR: invalid page in block 11 of relation base/16384/16462 A UNIQUE violation implies heap_update() succeeded, and heap_update() success implies changes to the old tuple header. Since the test catches us wrongly treating the page header as a tuple header, overwrites ensue as follows: Page header field | Overwritten as though it were this heap tuple header field ------------------------------------------------------------------------------ pd_lsn.xrecoff | xmax pd_lower | ctid.ip_blkid.bi_hi pd_upper | ctid.ip_blkid.bi_lo pd_special | ctid.ip_posid t_ctid=(11,38) caused those values of "lower", "upper", and "special". When the page is next read from disk, it gets "ERROR: invalid page". > I recommend (2) or (1), since they're self-contained. (3) or (4) potentially > give a better user experience for this rare failure, but that's code churn at > every DDL heap_update() site. How do you see it? I plan to write (1).
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Fix test races between syscache-update-pruned.spec and autovacuum.
- 42bb4fb7281e 17.5 landed
- 5bbc596391ae 18.0 landed
-
At update of non-LP_NORMAL TID, fail instead of corrupting page header.
- dc02b98bd1c8 15.11 landed
- 8bfd2cebab0a 13.19 landed
- 25e99483c411 14.16 landed
- f4af4515bb5f 17.3 landed
- 9311fcb86480 16.7 landed
- d28cd3e7b21c 18.0 landed
-
Disable runningcheck for src/test/modules/injection_points/specs.
- eca456edc1ca 17.3 landed
- 4f6ec3831d3e 18.0 landed
-
Merge copies of converting an XID to a FullTransactionId.
- 1587f7b9fc1a 17.3 landed
- 81772a495ec9 18.0 landed
-
Disable vacuum page skipping in selected test cases.
- c2dc1a79767a 14.0 cited