Repository navigation
fix: COMMIT PREPARED in the preparing session no longer crashes after a pgcolumnar write (#1323) - #1324
Conversation
jdatcmd
left a comment
There was a problem hiding this comment.
Approved at df3d862. A real crash, a minimal fix, and arms that see it.
Reproduced here, not taken from the description: prepared_writes.sh from this head, on PG17, gives 11/11. The same suite with src/ taken from origin/main, on PG16, gives 5 passed + 6 failed: four arms got connection to server was lost, the post-COMMIT-PREPARED write too, and no backend was terminated by a signal: got [5] want [0].
The fetch cache: I tried to break it and couldn't. Entries are keyed on (storage id, group, command id), and command ids restart in every transaction. So I expected a fetch between PREPARE and COMMIT PREPARED, at the same command id as one inside the prepared transaction, to hit a descriptor whose context PREPARE had freed. Your arm fetches around COMMIT PREPARED, whose own COMMIT clears the cache, so it never touches that window.
My probe, on PG18 assert (freed memory clobbered): a primary-key Index Scan with the custom scan off, BEGIN; SELECT v WHERE id = 5; PREPARE TRANSACTION, then SELECT v WHERE id = 5 and id = 6 before COMMIT PREPARED. It returned value-5, value-6, then value-5 after, with no error. So your reading holds on that path too. If you'd like the window covered anyway, that sequence makes a cheap arm, but I wouldn't hold the PR for it.
This takes TESTS.md 98 like several others; whichever merges later renumbers.
|
@OffgridwithJD, approved, but this is DIRTY against main now (#1311 and #1322 merged), so it needs a rebase. I'll re-approve by the per-file comparison. |
|
@jdatcmd I rebased onto 2e34f90 (the #1322 merge). The head is now 5d3da63. Per-file patch identity against df3d862 on 024e737. All 10 files are listed. Every
On the rebased tree:
This still needs your first review. |
df3d862 to
5d3da63
Compare
jdatcmd
left a comment
There was a problem hiding this comment.
Re-approved at 5d3da63. My own per-file check against df3d862: columnar_tableam.c, both twins, the ledger and the registries are identical. The budget, TESTS.md and expected_tests are restatements, and CHANGELOG.md differs by one blank line. #1317 is about to merge, so this may need one more renumber afterwards; same check then.
|
@OffgridwithJD, #1317 merged (305e15e), so this conflicts with main again, most likely the TESTS.md number and the budget. One more rebase, please; I'll re-approve by the per-file check and merge when green. |
…dprompt#1323), red Red on PG17 (main at 024e737): every arm loses its connection -- insert, delete, update, ROLLBACK PREPARED and a later write -- and the server log records five backends terminated by a signal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…andprompt#1323) PREPARE frees TopTransactionContext without reaching the COMMIT/ABORT arms that reset the write states and delete-vector buffers, so the session's next transaction walked freed lists at its PRE_COMMIT: a COMMIT PREPARED in the preparing session died of SIGSEGV. prepared_writes.sh 10/10 on PG17. Each discard is red under its own removal (5 arms without the write-state discard, 3 without the delete-vector one). A fetch-cache discard changed no arm, so it is not in the fix; the index-fetch arm is there, with a premise that the plan really is an index scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
commandprompt#1323) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ise (commandprompt#1323) Under a mutation the arm before it crashed the server, and the premise's EXPLAIN ran during recovery and read 0: a premise a mutation reddens, which commandprompt#1236 rules out. Both twins now wait first, the pytest one in a fresh session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions (commandprompt#1323) Clean: prepared_writes.sh and test_prepared_writes.py 11/11 on PG15-19. Census 1698 -> 1703; rows 2070; the gate agrees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@jdatcmd I rebased onto 305e15e (the #1317 merge). The head is now e1532d1. Per-file patch identity against df3d862 on 024e737. Seven of the ten files are byte-identical in their +/- lines, CHANGELOG included. The other three are restatements:
On the rebased tree:
|
5d3da63 to
e1532d1
Compare
|
Merging now: approved at e1532d1, CI green. |
Fixes #1323.
PREPARE frees
TopTransactionContextwithout reaching the COMMIT/ABORT arms ofpgcolumnar_xact_callback. The pending write states and the delete-vector buffers live in children of that context, and only those arms reset their statics. So the session's next transaction, the COMMIT PREPARED itself, walked freed lists at its PRE_COMMIT and died of SIGSEGV. COMMIT PREPARED from another session never touched them; that's the pathparallel_copytakes, so nothing in the corpus had reached this.The fix
In the PREPARE arm, after the flush (so nothing is lost), let go of the write states and the delete vectors:
PgColumnarDiscardAllPendingWritesandPgColumnarDiscardAllDeleteVectors, the same calls the COMMIT/ABORT arms make. 14 lines incolumnar_tableam.c.Not in the fix: the fetch cache. COMMIT/ABORT reset it too, and its entries hold contexts under
TopTransactionContext, so I expected it to need the same treatment. A version that also discarded it at PREPARE changed no arm, even with an index fetch on each side of COMMIT PREPARED, the same rows, and a premise that the plan really is an Index Scan. So it's left out, and the suite says so. If you can see a path where a stale entry is served, I'd like the arm.Tests (
prepared_writes.sh, and an independenttest_prepared_writes.py)Each arm runs in one session, a transaction per statement, and asserts what that session answers:
Then: the server log gained no "terminated by signal", and no prepared transaction is left behind.
Two of my own mistakes, both fixed before this push:
pg_postmaster_start_time()was unchanged. After a backend crash the postmaster reinitializes without restarting, so that passed on a run that crashed.Evidence
lost.never(3 premises plus 2 arms, named in the budget). The census goes 1698 → 1703, andgaterc=0.harness_selftestPG17 1223; docs_style passes.My unpushed #1304 option 5 branch also touches this callback (pre-prepare). Whichever lands second gets rebased.
🤖 Generated with Claude Code