| From: | Lionel Hamayon <lionel(dot)hamayon(at)evolution-digitale(dot)fr> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Cc: | Jasper Smit <jbsmit(at)gmail(dot)com>, Luc Vlaming Hummel <luc(dot)vlaming(at)servicenow(dot)com> |
| Subject: | Re: Assertion with aborted UPDATE in subtransaction |
| Date: | 2026-10-28 15:59:36 |
| Message-ID: | 179320248747.1093096.7208706909731646011@evolution-digitale.fr |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
I'd like to bring this thread back up. I ran into the same assertion
independently, and it still fails on master (4848fd6752a).
Why it matters to me: I develop an extension (pg_tviews) that keeps
summary tables up to date from statement-level AFTER triggers with
transition tables. The triggers upsert with INSERT ... ON CONFLICT DO
UPDATE, and some run inside PL/pgSQL exception blocks that roll back an
earlier update of the same row. I run my randomized correctness tests
on assert-enabled builds (16, 18 and 19), and this assertion crashes
the backend there. In practice I can't run that suite on cassert
builds until it's fixed. As Luc found, I saw no wrong results on
non-assert builds.
Here is a smaller repro than the original script (assert-enabled build,
fresh database):
CREATE TABLE t (a int, b text);
INSERT INTO t VALUES (1, '');
BEGIN;
SELECT * FROM t FOR NO KEY UPDATE;
SAVEPOINT s;
UPDATE t SET a = 2;
ROLLBACK TO SAVEPOINT s;
RELEASE SAVEPOINT s;
UPDATE t SET b = repeat('x', 3000); -- TRAP
COMMIT;
TRAP: failed Assert("HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple)"),
File: "heapam.c", Line: 3893
It fails on master, REL_19_STABLE, 18.6 and REL_14_STABLE. The original
assert.sql no longer triggers it on REL_19 for me, because the page
never fills up, so the repro above uses a value over the TOAST threshold
instead.
Jasper's multixact.patch still applies cleanly to master, and with it:
- the repro above and the variants I tried pass (FOR UPDATE with a
key-changing update, ON CONFLICT DO UPDATE, MERGE, PL/pgSQL exception
blocks, a full page);
- make check and the isolation tests pass;
- on Luc's cost question: rebuilding a multixact with the same members
returns the existing one from the backend-local cache, so no extra
multixact IDs are used. I ran 500 TOAST updates while another
transaction held FOR KEY SHARE on the row, and both the patched and
unpatched builds used 2 multixacts. What remains is one
TransactionIdIsInProgress() call per member, and only on this path,
which as far as I can tell is reachable only from heap_update()'s
TOAST/new-page branch.
The patch still needs its comments updated: the function header and the
comment above the loop both still say the existing multixact is
returned as-is.
Attached is a regression test for combocid.sql that goes on top of
Jasper's patch. It has three cases, and each one crashes an
assert-enabled build without the fix.
In full disclosure: I'm not a PostgreSQL internals developer. The
analysis and the test were done with an AI assistant (Claude). I checked
the results by running them, but I can't vouch for the reasoning in
depth, so please treat it accordingly. I hope that, together with
Jasper's patch, this is enough to get the fix in.
Regards,
Lionel Hamayon
| Attachment | Content-Type | Size |
|---|---|---|
| regress-lock-only-xmax.patch | text/x-patch | 5.5 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Greg Burd | 2026-10-28 16:00:11 | Re: Quickselect instead of a heap for ORDER BY ... LIMIT N large bounded sorts |
| Previous Message | Andres Freund | 2026-10-28 15:57:53 | Re: WAL record CRC calculated incorrectly because of underlying buffer modification |