BUG #19760, part 2: VACUUM cost of the second HEAP_XMAX_INVALID hint One run on 2026-10-25, 12 interleaved rounds per build, with Michael's scripts from upthread (setup.sql, holders.sql, delete.sql, subxact_updates.sql, vacuum.sql), unchanged, plus subxact_aborts.sql below. Same harness and method as the measurements.txt of 2026-10-22. Base: master 871dee0ccbe, the base of those measurements. Builds: configure --enable-depend, CFLAGS=-O2, no assertions. HEAD master v2+0001 v2-0001 from upthread + nocfbot-0001 (xmin != xmax, MultiXact skip): the second hint with v2's check, !TransactionIdIsInProgress() && !TransactionIdDidCommit() old 0002 + the clog-only TransactionIdDidAbort() check v3 + the fixed check: TransactionIdDidAbort(xmax) && !TransactionIdIsInProgress(xmax) The heapam_visibility.c of "v3" is the one of v3-0001 on today's master, byte for byte, except for two lines of a test-only injection point that these builds do not compile in (the object code differs only in the __LINE__ constants of two elog() calls). 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. 64 holders. Metric: per sample, the mean of the 9 VACUUM times of vacuum.sql (or the first one / the other 8, where said). Reported: median of the samples, ratio to HEAD, bootstrap 95% CI of the ratio of medians (4000 resamples). 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. delete.sql: build n median ms x HEAD 95% CI HEAD 12 418.9 1.00 v2+0001 11 419.0 1.00 0.99..1.01 old 0002 11 417.8 1.00 0.99..1.00 v3 12 419.7 1.00 0.99..1.01 subxact_updates.sql: build n median ms x HEAD 95% CI HEAD 9 923.6 1.00 v2+0001 9 1396.6 1.51 1.50..1.55 old 0002 9 931.5 1.01 0.99..1.03 v3 9 924.8 1.00 0.99..1.02 subxact_aborts.sql, first VACUUM only: build n median ms x HEAD 95% CI HEAD 12 5869.6 1.00 v2+0001 12 6295.6 1.07 1.07..1.07 old 0002 12 5877.2 1.00 1.00..1.00 v3 12 6294.1 1.07 1.07..1.07 subxact_aborts.sql, VACUUMs 2-9: build n median ms x HEAD 95% CI HEAD 12 5430.5 1.00 v2+0001 12 5431.9 1.00 1.00..1.00 old 0002 12 5434.1 1.00 1.00..1.00 v3 12 5430.5 1.00 1.00..1.00 In subxact_updates.sql xmax is a released subtransaction that is still running: it is not aborted in the clog, so v3 never asks the proc array. In subxact_aborts.sql xmax has aborted: v3 pays the proc array check once per tuple, in the first VACUUM, which sets the hint. subxact_updates.sql had 3 rounds with no clean sample (n = 9). subxact_aborts.sql (like subxact_updates.sql, but each subtransaction rolls back): \if :{?n} \else \set n 500000 \endif BEGIN; INSERT INTO t SELECT g, 0 FROM generate_series(1, :n) g; DO $$ DECLARE r record; BEGIN FOR r IN SELECT ctid AS c FROM t LOOP BEGIN UPDATE t SET v = 1 WHERE ctid = r.c; RAISE EXCEPTION 'roll back this subtransaction'; EXCEPTION WHEN others THEN NULL; END; END LOOP; END $$;