BUG #19760: cost of the second SetHintBits() of v2-0001, and two diffs on top of v2 Run with Michael's scripts from upthread, as attached there (setup.sql, holders.sql, delete.sql, insert_only.sql, subxact_updates.sql, vacuum.sql), unchanged: 500k rows, 64 holders. Base: master 871dee0ccbe, plus v2-0001 and v2-0002 from upthread. Builds: configure --enable-depend, CFLAGS=-O2, no assertions. Machine: Ryzen AI 9 HX 370 laptop, runs pinned to 4 Zen 5 cores (8 threads); data directory on tmpfs. Server settings: max_connections = 200, autovacuum = off, max_wal_size = 256MB, the rest default. One sample: psql -f setup.sql for i in $(seq $H); do psql -X -f holders.sql > /dev/null 2>&1 & done (wait until $H backends sleep in pg_sleep) psql: \i .sql -- left open CHECKPOINT; SELECT pg_stat_reset_slru(); psql -X -f vacuum.sql -- metric: mean of its 9 VACUUM times read pg_stat_slru, then stop everything Each round runs every build once, in an order rotated per round. A sample is dropped and redone if a CPU calibration loop run before and after it is more than 8% slower than its idle time. Reported: median of the samples, ratio to HEAD, bootstrap 95% CI of the ratio of medians (4000 resamples). Builds: HEAD master v2 v2-0001 + v2-0002 as posted v2 1st hint only v2 without the second SetHintBits() v2 + skip xmax==xmin second hint only when xmax != xmin v2 + skip xmax!=xmin second hint only when xmax == xmin v2 + catalogs only second hint only if IsCatalogRelationOid(htup->t_tableOid) v2 + DidAbort second hint if TransactionIdDidAbort(xmax), instead of !InProgress && !DidCommit v2 + 0001 skip xmax == xmin and MultiXact xmax v2 + 0001 + 0002 0001, then TransactionIdDidAbort(xmax) delete.sql, 64 holders: build n median ms x HEAD 95% CI HEAD 12 424.4 1.00 v2 12 841.2 1.98 1.96..2.02 v2 1st hint only 12 420.0 0.99 0.98..1.01 v2 + skip xmax==xmin 12 424.2 1.00 0.99..1.01 v2 + skip xmax!=xmin 12 838.1 1.98 1.95..2.05 v2 + catalogs only 12 422.6 1.00 0.99..1.01 v2 + DidAbort 12 429.4 1.01 1.00..1.03 v2 + 0001 12 424.1 1.00 0.99..1.02 v2 + 0001 + 0002 12 423.7 1.00 0.98..1.02 subxact_updates.sql, 64 holders: build n median ms x HEAD 95% CI HEAD 12 875.1 1.00 v2 12 1338.3 1.53 1.51..1.55 v2 1st hint only 11 876.0 1.00 0.99..1.01 v2 + skip xmax==xmin 11 1335.0 1.53 1.51..1.56 v2 + skip xmax!=xmin 12 877.7 1.00 0.99..1.03 v2 + catalogs only 12 882.1 1.01 1.00..1.03 v2 + DidAbort 11 884.6 1.01 1.00..1.02 v2 + 0001 12 1336.0 1.53 1.51..1.55 v2 + 0001 + 0002 12 883.4 1.01 1.00..1.03 insert_only.sql, 64 holders: build n median ms x HEAD 95% CI HEAD 12 419.6 1.00 v2 12 418.8 1.00 0.99..1.01 v2 1st hint only 12 416.2 0.99 0.98..1.00 v2 + skip xmax==xmin 12 417.7 1.00 0.99..1.01 v2 + skip xmax!=xmin 12 418.4 1.00 0.99..1.01 v2 + catalogs only 12 419.6 1.00 0.99..1.01 v2 + DidAbort 12 419.8 1.00 0.99..1.01 v2 + 0001 12 417.5 1.00 0.99..1.01 v2 + 0001 + 0002 12 416.7 0.99 0.98..1.01 pg_stat_slru blks_hit+blks_read per VACUUM, delete.sql, 64 holders: build clog subtrans HEAD 0 0 v2 0 0 v2 + 0001 + 0002 0 0 pg_stat_slru blks_hit+blks_read per VACUUM, subxact_updates.sql, 64 holders: build clog subtrans HEAD 499936 999872 v2 999872 1999744 v2 + 0001 + 0002 999936 999872 delete.sql, holders sweep (median ms, x HEAD): holders HEAD v2 v2 + 0001 v2+0001+0002 0 12 17 (1.51) 12 (1.03) 12 (1.02) 16 117 229 (1.95) 118 (1.01) 120 (1.02) 64 424 841 (1.98) 424 (1.00) 424 (1.00) 128 823 1659 (2.02) 823 (1.00) 836 (1.01) subxact_updates.sql, holders sweep (median ms, x HEAD): holders HEAD v2 v2 + 0001 v2+0001+0002 0 50 83 (1.65) 83 (1.67) 57 (1.14) 16 268 412 (1.54) 412 (1.54) 276 (1.03) 64 875 1338 (1.53) 1336 (1.53) 883 (1.01) 128 1748 2644 (1.51) 2645 (1.51) 1756 (1.00) Correctness, on the same variants built with --enable-cassert --enable-injection-points: 1 v2-0002 isolation spec, both permutations 2 Michael's ANALYZE + CLUSTER pg_statistic sequence: 1 row 3 VACUUM FULL pg_class while the CREATE TABLE transaction is open, after ALTER TABLE rolled back to a savepoint: the pg_class row survives the COMMIT 4 Zhijie's MultiXact / FOR KEY SHARE repro (his injection point and spec): judged on the last step, FOR UPDATE NOWAIT must fail while FOR KEY SHARE is held 5 logical decoding (test_decoding) across case 3 plus an INSERT: both INSERTs decoded, no error 6 make check, isolation check, recovery 035 Same as 3, the pg_class row must exist once with the right relnatts: 7 ALTER in a subtransaction that was released 8 ALTER in an inner savepoint, released, then the outer one rolled back 9 as 3, then PREPARE TRANSACTION before the VACUUM FULL 10 as 7, then PREPARE TRANSACTION before the VACUUM FULL 11 one ALTER released, then a second ALTER rolled back build 1 2 3 4 5 6 7 8 9 10 11 HEAD F F F . F . . F F . F v2 . . . F . . . . . . . v2 1st hint only F . F . F . . F F . F v2 + skip xmax==xmin . . . F . . . . . . . v2 + skip xmax!=xmin F . F . F . . F F . F v2 + catalogs only . . . * . . . . . . . v2 + DidAbort . . . F . . . . . . . v2 + 0001 . . . . . . . . . . . v2 + 0001 + 0002 . . . . . . . . . . . (. pass, F fail) * passes only because the repro table is not a catalog, so the new code is never reached; on a catalog it keeps v2's race. Mutant check: 0001 + 0002 with the hint set unconditionally passes cases 2, 3 and 5, and fails 7, 10 and 11 with two pg_class rows for the table (old and new version both kept).