post-recovery amcheck expectations
Noah Misch <noah@leadboat.com>
From: Noah Misch <noah@leadboat.com>
To: pgsql-hackers@postgresql.org
Date: 2023-10-05T02:52:32Z
Lists: pgsql-hackers
Attachments
- amcheck-post-recovery-v0.patch (text/plain) patch v0
Suppose we start with this nbtree (subset of a diagram from verify_nbtree.c):
* 1
* / \
* 2 <-> 3
We're deleting 2, the leftmost leaf under a leftmost internal page. After the
MARK_PAGE_HALFDEAD record, the first downlink from 1 will lead to 3, which
still has a btpo_prev pointing to 2. bt_index_parent_check() complains here:
/* The first page we visit at the level should be leftmost */
if (first && !BlockNumberIsValid(state->prevrightlink) && !P_LEFTMOST(opaque))
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("the first child of leftmost target page is not leftmost of its level in index \"%s\"",
RelationGetRelationName(state->rel)),
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
state->targetblock, blkno,
LSN_FORMAT_ARGS(state->targetlsn))));
One can encounter this if recovery ends between a MARK_PAGE_HALFDEAD record
and its corresponding UNLINK_PAGE record. See the attached test case. The
index is actually fine in such a state, right? I lean toward fixing this by
having amcheck scan left; if left links reach only half-dead or deleted pages,
that's as good as the present child block being P_LEFTMOST. There's a
different error from bt_index_check(), and I've not yet studied how to fix
that:
ERROR: left link/right link pair in index "not_leftmost_pk" not in agreement
DETAIL: Block=0 left block=0 left link from block=4294967295.
Alternatively, one could view this as a need for the user to VACUUM between
recovery and amcheck. The documentation could direct users to "VACUUM
(DISABLE_PAGE_SKIPPING off, INDEX_CLEANUP on, TRUNCATE off)" if not done since
last recovery. Does anyone prefer that or some other alternative?
For some other amcheck expectations, the comments suggest reliance on the
bt_index_parent_check() ShareLock. I haven't tried to make test cases for
them, but perhaps recovery can trick them the same way. Examples:
errmsg("downlink or sibling link points to deleted block in index \"%s\"",
errmsg("block %u is not leftmost in index \"%s\"",
errmsg("block %u is not true root in index \"%s\"",
Thanks,
nm
Commits
-
amcheck: Distinguish interrupted page deletion from corruption.
- f31ccb575ed6 13.13 landed
- e04509f3246d 11.22 landed
- 42496cba60bc 14.10 landed
- 3a205c91665f 12.17 landed
- 6f81386a9cea 15.5 landed
- 3c6a05b805c7 16.1 landed
- 6ec9e9975eff 17.0 landed
-
Improve checking of child pages in contrib/amcheck.
- d114cc538715 13.0 cited