> If you subtract recently dead from that number within the heap > implementation, then it will no longer > reflect non-removable tuples and the log message in the cluster > function "found %.0f removable, %.0f nonremovable row versions" will no > longer be correct.
Yes, that's correct. I did not pay attention to the logging aspect. Here is a test with and without the patch. While there is a long-running serializable transaction in another session, running an update followed by a normal vacuum sets the reltuples value correctly to 10. A follow-up VACUUM FULL then sets it incorrectly to 14. The patch, which sets the num_tuples after the logging, ensures that the logging is correct and that pg_class.reltuples matches the actual number of live tuples. -- without the patch test=# UPDATE stats SET a=1 WHERE a > 6; UPDATE 4 test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- -1 (1 row) test=# vacuum verbose stats; INFO: vacuuming "test.public.stats" INFO: finished vacuuming "test.public.stats": index scans: 0 pages: 0 removed, 1 remain, 1 scanned (100.00% of total), 0 eagerly scanned tuples: 0 removed, 14 remain, 4 are dead but not yet removable removable cutoff: 789, which was 1 XIDs old when operation ended new relfrozenxid: 788, which is 1 XIDs ahead of previous value frozen: 0 pages from table (0.00% of total) had 0 tuples frozen visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-visible) index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed avg read rate: 0.000 MB/s, avg write rate: 9.159 MB/s buffer usage: 14 hits, 0 reads, 3 dirtied WAL usage: 4 records, 3 full page images, 24981 bytes, 0 buffers full system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s VACUUM test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- 10 (1 row) test=# VACUUM (verbose, FULL) stats; INFO: vacuuming "public.stats" INFO: "public.stats": found 0 removable, 14 nonremovable row versions in 1 pages DETAIL: 4 dead row versions cannot be removed yet. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- 14 (1 row) -- with the patch test=# UPDATE stats SET a=1 WHERE a > 6; UPDATE 4 test=# test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- -1 (1 row) test=# VACUUM verbose stats; INFO: vacuuming "test.public.stats" INFO: finished vacuuming "test.public.stats": index scans: 0 pages: 0 removed, 1 remain, 1 scanned (100.00% of total), 0 eagerly scanned tuples: 0 removed, 14 remain, 4 are dead but not yet removable removable cutoff: 794, which was 1 XIDs old when operation ended new relfrozenxid: 793, which is 1 XIDs ahead of previous value frozen: 0 pages from table (0.00% of total) had 0 tuples frozen visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-visible) index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed avg read rate: 0.000 MB/s, avg write rate: 9.195 MB/s buffer usage: 17 hits, 0 reads, 3 dirtied WAL usage: 4 records, 3 full page images, 24981 bytes, 0 buffers full system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s VACUUM test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- 10 (1 row) test=# VACUUM (verbose, FULL) stats; INFO: vacuuming "public.stats" INFO: "public.stats": found 0 removable, 14 nonremovable row versions in 1 pages DETAIL: 4 dead row versions cannot be removed yet. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM test=# SELECT reltuples FROM pg_class WHERE relname = 'stats'; reltuples ----------- 10 (1 row) -- Sami