datfrozenxid > relfrozenxid w/ crash before XLOG_HEAP_INPLACE

Noah Misch <noah@leadboat.com>

From: Noah Misch <noah@leadboat.com>
To: pgsql-hackers@postgresql.org
Date: 2024-06-20T01:29:08Z
Lists: pgsql-hackers

Attachments

https://postgr.es/m/20240512232923.aa.nmisch@google.com wrote:
> Separable, nontrivial things not fixed in the attached patch stack:

> - Trouble is possible, I bet, if the system crashes between the inplace-update
>   memcpy() and XLogInsert().  See the new XXX comment below the memcpy().

That comment:

	/*----------
	 * XXX A crash here can allow datfrozenxid() to get ahead of relfrozenxid:
	 *
	 * ["D" is a VACUUM (ONLY_DATABASE_STATS)]
	 * ["R" is a VACUUM tbl]
	 * D: vac_update_datfrozenid() -> systable_beginscan(pg_class)
	 * D: systable_getnext() returns pg_class tuple of tbl
	 * R: memcpy() into pg_class tuple of tbl
	 * D: raise pg_database.datfrozenxid, XLogInsert(), finish
	 * [crash]
	 * [recovery restores datfrozenxid w/o relfrozenxid]
	 */

>   Might solve this by inplace update setting DELAY_CHKPT, writing WAL, and
>   finally issuing memcpy() into the buffer.

That fix worked.  Along with that, I'm attaching a not-for-commit patch with a
test case and one with the fix rebased on that test case.  Apply on top of the
v2 patch stack from https://postgr.es/m/20240617235854.f8.nmisch@google.com.
This gets key testing from 027_stream_regress.pl; when I commented out some
memcpy lines of the heapam.c change, that test caught it.

This resolves the last inplace update defect known to me.

Thanks,
nm

Commits

  1. WAL-log inplace update before revealing it to other sessions.

  2. Comment on need to MarkBufferDirty() if omitting DELAY_CHKPT_START.