Skip to content

test: a prepared transaction, seen from other sessions and across a crash (#1341; stacked on #1422) - #1428

Merged
jdatcmd merged 3 commits into
fix/1421-unique-update-40001from
test/1341-prepared-sessions
Oct 15, 2026
Merged

jdatcmd merged 3 commits into
fix/1421-unique-update-40001from
test/1341-prepared-sessions

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1422. The base branch is fix/1421-unique-update-40001, so this diff shows only this PR's commit. Merge #1422 first; GitHub then retargets this PR to main.

Covers the #1341 core-am item "beyond the same-session cases in prepared_writes". Test-only.

What it adds

A new suite, test/prepared_across_sessions.sh, and its pytest twin, test_prepared_across_sessions.py. The twin runs on a private cluster, because the last arm crashes the postmaster. The table has a primary key.

arm result
a prepared insert, seen from another session invisible (0 rows)
a second insert of a key the prepared transaction holds waits, then 23505 after COMMIT PREPARED; one row, the prepared one
after COMMIT PREPARED all 100 prepared rows visible
a prepared DELETE, seen from another session every row still visible
ROLLBACK PREPARED of a DELETE, and of an UPDATE every row as it was, none duplicated
an UPDATE of a row a prepared transaction updated waits, then 40001 after COMMIT PREPARED
a prepared insert, then an immediate crash cluster back, pg_prepared_xacts still lists it, and after COMMIT PREPARED its 5000 rows equal a heap table's

Every wait is observed (wait_event_type = 'Lock'), not slept on.

Evidence

🤖 Generated with Claude Code

https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdatcmd Reviewed ec778b4 (its one commit on top of #1422).

  • What it adds: prepared_across_sessions.sh and its twin. Each arm observes its wait, and the crash arm checks that the cluster restarted (ok) and that pg_prepared_xacts still lists the transaction.

  • Reproduced red first (PG17): on main's src, 9 + 1 failed: an UPDATE of a row a prepared transaction updated waits, then fails with 40001 after COMMIT PREPARED: got [yes|23505|1/prepared] want [yes|40001|1/prepared]. The twin is red by the same name. That is the ledger's one dated row.

  • Green: 10/10, and the twin 1 passed, on PG17 and PG18 with #1422's code. The census, 2795 / 2108 / 687, matches the tsv, and cluster_tests 567 matches collection.

  • Blocker: the three ROLLBACK PREPARED checks in the shell never assert that anything was prepared, and the two harnesses disagree on it. psql_run's status is ignored. If the BEGIN; DELETE ...; PREPARE TRANSACTION string fails, nothing runs, and "another session still sees every row" and both "leaves every row as it was" checks compare the unchanged table with itself. The twin's _run raises instead. Measured on PG17, with the PREPAREs for px_del and px_upd turned into syntax errors in both harnesses:

    shell pytest
    neither transaction ever prepared 10 passed + 0 failed 1 failed (SyntaxError)

    The crash arm already states its premise (pg_prepared_xacts ... gid = 'px_crash' = 1). The same assertion for px_del and for px_upd while each is prepared, in both twins, would make these arms say what they claim.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd force-pushed the fix/1421-unique-update-40001 branch from 34ccb8a to 4323c77 Compare October 15, 2026 16:50
jdatcmd and others added 3 commits October 15, 2026 17:18
…rash (#1341)

prepared_writes covers PREPARE and COMMIT PREPARED in one session. A new
suite, prepared_across_sessions, and its pytest twin (on a private
cluster) cover the rest:

- while a transaction is prepared, another session does not see its
  insert and still sees the rows its DELETE removed;
- a second insert of a key it holds waits, then gets 23505 after COMMIT
  PREPARED;
- an UPDATE of a row it updated waits, then gets 40001;
- ROLLBACK PREPARED of a DELETE and of an UPDATE leaves every row as it
  was;
- a prepared insert survives an immediate crash, is still listed, and
  after COMMIT PREPARED its rows equal a heap table's.

Stacked on #1422: the 40001 arm needs that fix, and on main it reads
23505. 10/10 on each of PG15-19, and the twin on each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
…ransaction was prepared, in both twins (#1341)

Review of #1428 by OffgridwithJD: with the PREPAREs for px_del and px_upd
broken, the shell passed 10/10 by comparing the unchanged table with itself,
and the twin failed with a SyntaxError instead of a named check. Each now
asserts pg_prepared_xacts lists its gid while it is prepared, and the twin
leaves a failed PREPARE for that premise to report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… new head (#1341)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd

jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, addressed in 64eb1fd, now stacked on #1422's new head 4323c77.

  • Both ROLLBACK PREPARED arms assert their transaction was prepared, in both twins: premise: the DELETE is prepared and premise: the UPDATE is prepared. Each checks that pg_prepared_xacts lists its gid while it is prepared. In the twin, those two PREPAREs no longer raise; a failure is left for the premise to report, as in the shell.
  • Your case (PG17): with both PREPAREs turned into syntax errors in both harnesses, the shell gives 10 passed + 2 failed (premise: the DELETE is prepared: got [0] want [1] and the same for the UPDATE). The twin fails on premise: the DELETE is prepared: got 0 want 1. The twins now agree.
  • Green: 12/12 in both twins on each of PG15 to PG19.
  • Bookkeeping: the two premises enter as never. The census is 2888 + 12 = 2900 rows, 2185 + 11 = 2196 never, 703 + 1 = 704 dated, matching the tsv. cluster_tests is 570 by collection, and the guard set passes 435/435.
  • The gate: against origin/fix/1421-unique-update-40001, this PR's base, it gives rc=0. Against main it refuses suites_not_covered 232 because test: the grouped node's float sums raise on overflow, as core's do (#1341 item 50) #1411 has since lowered main to 231. That clears when fix: a concurrent same-row UPDATE gets 40001, not 23505, on a table with a unique index (#1421) #1422 is restated on main.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd force-pushed the test/1341-prepared-sessions branch from ec778b4 to 64eb1fd Compare October 15, 2026 17:21

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 64eb1fd, stacked on #1422.

  • The premises do their job. On PG17, with the px_del and px_upd PREPAREs turned into syntax errors in both harnesses, the shell gives 10 passed + 2 failed: premise: the DELETE is prepared: got [0] want [1] and premise: the UPDATE is prepared: got [0] want [1]. The twin fails on premise: the DELETE is prepared: got 0 want 1, so the twins now agree.
  • Green: 12/12 in the shell and the twin 1 passed at head, on PG17. Restored after the mutation: 12/12.
  • Census: 2900 / 2196 / 704 from the tsv, matching the paragraph's 2900 / 2196 / 704.

It will need restating once #1422 is rebased onto 8de3d5f; you've already noted that the gate's suites_not_covered refusal comes from that.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit 1d6b236 into fix/1421-unique-update-40001 Oct 15, 2026
15 checks passed
jdatcmd added a commit that referenced this pull request Oct 15, 2026
…ith a unique index (#1421), with #1428 stacked on it

Squashed for the rebase onto main; it replaces:
- docs: the CHANGELOG states the keyed UPDATE's cost as both of us measured it (#1421)
- test: the two prepared premises enter the ledger as never, on #1422's new head (#1341)
- test: prepared_across_sessions asserts each ROLLBACK PREPARED arm's transaction was prepared, in both twins (#1341)
- test: a prepared transaction, seen from other sessions and across a crash (#1341)
- test: the memory arms enter the ledger, two dated, and the counts follow (#1421)
- fix: the per-row serialization check frees its catalog reads (#1421)
- test: a keyed UPDATE over groups with deletes keeps its memory bounded, in both twins (#1421)
- docs: split a limitations.md sentence the plain-language gate measured as too long
- fix: a concurrent same-row UPDATE gets 40001, not 23505, on a table with a unique index (#1421)
- test: a concurrent same-row UPDATE must get 40001 with a unique index too (#1421, red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jdatcmd added a commit that referenced this pull request Oct 15, 2026
…ith a unique index (#1421), with #1428 stacked on it

Squashed for the rebase onto main; it replaces:
- docs: the CHANGELOG states the keyed UPDATE's cost as both of us measured it (#1421)
- test: the two prepared premises enter the ledger as never, on #1422's new head (#1341)
- test: prepared_across_sessions asserts each ROLLBACK PREPARED arm's transaction was prepared, in both twins (#1341)
- test: a prepared transaction, seen from other sessions and across a crash (#1341)
- test: the memory arms enter the ledger, two dated, and the counts follow (#1421)
- fix: the per-row serialization check frees its catalog reads (#1421)
- test: a keyed UPDATE over groups with deletes keeps its memory bounded, in both twins (#1421)
- docs: split a limitations.md sentence the plain-language gate measured as too long
- fix: a concurrent same-row UPDATE gets 40001, not 23505, on a table with a unique index (#1421)
- test: a concurrent same-row UPDATE must get 40001 with a unique index too (#1421, red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants