Re: BUG #19620: pg_class index corruption caused by statement_timeout during VACUUM FULL

From: Andrey Borodin <x4mmm(at)yandex-team(dot)ru>
To: hackerzheng666(at)gmail(dot)com, PostgreSQL mailing lists <pgsql-bugs(at)lists(dot)postgresql(dot)org>
Subject: Re: BUG #19620: pg_class index corruption caused by statement_timeout during VACUUM FULL
Date: 2026-08-16 16:01:18
Message-ID: D030A6DD-9945-4206-9DA1-832C56B52B88@yandex-team.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

> On 15 Aug 2026, at 05:37, PG Bug reporting form <noreply(at)postgresql(dot)org> wrote:
>
> What is new: all known reports require concurrency (parallel workers,
> concurrent connections, concurrent REINDEX). This reproducer triggers
> the same corruption in a single session with all parallelism disabled,
> via statement cancellation alone. This is an interrupt-safety bug in
> catalog index maintenance, distinct from the concurrency races in the
> known reports.

Hi Zheng,

Thanks for the report. This is worth fixing for sure, thanks for raising
attention to this.

I believe this is a known bug, unfixed since July 2020 [0]. Justin
Pryzby hit the same duplicate-TID corruption in a pg_class index, and
Peter Geoghegan and Tom Lane diagnosed it there; Peter also pointed at
that thread when BUG #18490 arrived in 2024 [1]. The mechanism is in
heapam_index_build_range_scan(): for a HEAPTUPLE_INSERT_IN_PROGRESS
tuple inserted by another transaction, the build waits only when it is
checking uniqueness. So for a non-unique index our opinion of which HOT
chain member is live can change mid-scan, and the build emits two
entries for one chain, both carrying the chain root's TID.

Worth adding, since that thread only ever discussed the assertion: this
is not confined to assert builds. I reproduced on REL_18_STABLE with
two sessions, one looping REINDEX INDEX
pg_class_tblspc_relfilenode_index and one looping CREATE TABLE /
CREATE INDEX / DROP TABLE, with no statement_timeout anywhere. A
cassert build trips comparetup_index_btree_tiebreak() in 0.4 seconds. A
build without assertions completes the rebuild and leaves the catalog
index corrupt on disk, where amcheck finds it after 23 iterations:

ERROR: posting list contains misplaced TID in index
"pg_class_tblspc_relfilenode_index"
DETAIL: Index tid=(1,210) posting list offset=1 page lsn=0/1C2A950.

So bt_index_check() on catalog indexes seems worth running in
production....

Now the thing to check. I suspect your reproducer is concurrent after
all, which would make this the same bug rather than a new one.
max_worker_processes = 0 does not turn autovacuum off. With that
setting autovacuum and autoanalyze are still running, I see 6
on pg_class itself within half a minute. Some of those updates
a pg_class row without touching an indexed column, which is a HOT
update on pg_class, and that is precisely the concurrent writer the
2020 mechanism needs. The code also seems to require one: both branches
that can emit a second entry for a chain sit inside
"if (!TransactionIdIsCurrentTransactionId(xwait))", so a backend acting
alone should not be able to reach them.

Could you set autovacuum = off in postgresql.conf, rather than relying
on max_worker_processes, and re-run your loop? If it still corrupts,
then you do have something new and I would very much like to see it. It
would also help to know the xmin of the two heap tuples your duplicate
pointers reference, and whether that XID belongs to your session.

I could not reproduce a single-session failure myself, but that is weak
evidence either way, so I would rather have your answer than my guess.

Thank you!

Best regards, Andrey Borodin.

[0] https://www.postgresql.org/message-id/20200728151002.GE20393%40telsasoft.com
[1] https://www.postgresql.org/message-id/CAH2-WzkaOCUV_0JYmbv%3DZKH5cs6X27eKZLouWq0d6_meGyNJtg%40mail.gmail.com

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Michael Paquier 2026-08-16 22:26:26 Re: BUG #19612: SEGV in ParseConfigFp() in guc-file.l
Previous Message Andrey Borodin 2026-08-16 15:03:15 Re: BUG #19613: pg_restore: several SEGVs in ReadToc() in pg_backup_archiver.c