Hi Zheng, hi Andrey,
Zheng's autovacuum=off result matches what I see: this is the July 2020
catalog HOT / index-build race, not a new interrupt-safety bug.
statement_timeout only causes VACUUM FULL of pg_class to retry while
autovacuum is still HOT-updating catalog rows.
Waiting for INSERT_IN_PROGRESS is not usable on catalogs. Commit
1ddc2703a936 stopped those waits on non-unique builds because VACUUM
FULL / CLUSTER takes AccessExclusiveLock on the catalog and then
deadlocks with a backend whose in-progress insert we would wait for.
VACUUM FULL also sets skip_constraint_checks, so even unique pg_class
indexes take the non-waiting path. That is why the original report
could corrupt pg_class_relname_nsp_index.
The attached patch keeps that decision. heapam_index_build_range_scan
records which root offsets on the current page have already been
handed to the AM, and skips a second callback for the same root TID.
The mark is taken only after a partial-index predicate would accept
the tuple. Refreshing root_offsets for an unknown HOT parent does not
clear the map.
I reproduced this on current master with cassert. With about 14000
pg_class rows, REINDEX INDEX pg_class_tblspc_relfilenode_index against
GRANT/REVOKE on one table (relacl is not indexed, so the update is
HOT) traps without the patch in comparetup_index_btree_tiebreak
("ItemPointer values should never be equal") within a few seconds.
The same workload with the patch does not trap. bt_index_check and
bt_index_parent_check pass, and VACUUM FULL pg_class still completes
while another transaction has an open catalog insert.
contrib/amcheck/t/007_catalog_reindex_hot.pl covers the deadlock
invariant and the concurrent REINDEX + GRANT case.
The patch is against master.
Thanks,
Marcelo Tesla