Skip to content

test: native_backend_crash gets its pytest twin, and a mid-write kill must follow a write (#1341 item 25) - #1444

Merged
jdatcmd merged 2 commits into
mainfrom
test/1341-native-backend-crash-twin
Oct 16, 2026
Merged

jdatcmd merged 2 commits into
mainfrom
test/1341-native-backend-crash-twin

Conversation

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

#1341 item 25, its second suite: one backend dies, the postmaster lives, and committed columnar data is intact (#217). native_backend_crash.sh had no pytest twin. This PR adds one, every check by the shell's name, and seeds the suite into the ledger. The port found that the mid-write kill could land on a write that hadn't written anything yet, and that is fixed in both twins.

Stacked on #1443, which is stacked on #1441 and #1438. Merge them in that order; this branch is #1443's head plus two commits.

What changes

  • test/pytest/test_native_backend_crash.py, the twin. It has three tests, one per kill point, on a private cluster, since the reinit drops every session on the server.
    • The kill is os.kill(pid, SIGSEGV) on the inserting backend, whose pid the connection reports.
    • The sentinel and the second reader are sessions parked in pg_sleep on their own threads. One counts as dropped when the server ended its statement with an error.
    • A session that a failed check leaves running is terminated before its connection closes. Without this, closing a connection under a running statement crashed the driver (measured: pytest died with SIGSEGV under the no-kill mutant). A writer left running also held its locks into the next test.
    • compare_to_bash.py grades it as covering every shell name, and it joins COMPLETE.
  • The mid-write kill used to wait a fixed 0.5s. Nothing showed the INSERT had written a page by then.
    • Measured: with the inserting session's stripe_row_limit set above its row count, the write buffers whole until its end. The kill then landed on a write that had produced nothing, and the old suite stayed 25/25.
    • The fix: the kill now waits for pg_relation_size('c') to grow past its size before the INSERT. Each crash asserts <marker>: the INSERT had written pages before the kill (the relation grew).
    • With the same setting now: crashA/crashC: kill lands mid-write go red, because pages exist only once the backend has reached its pg_sleep.
  • Ledger: native_backend_crash is seeded with 28 rows from five-major runs, and 3 of the 28 rows dated. suites_not_covered goes 221 → 220, and the census is 3564 + 28 = 3592 rows, 2639 + 25 = 2664 never and 925 + 3 = 928 dated, matching the tsv. The gate gives rc=0 against origin/main.

Red first

Each mutation was run one at a time on PG18, through both twins, with the .so md5 printed every time.

mutation native_backend_crash.sh test_native_backend_crash.py the arms that went red
none (the head) 28/28 28/28 none
a stripe data page written with no WAL record (columnar_storage.c:511) 25/28 all 3 tests fail each scenario's committed content in c matches the heap mirror (corrupt encoded chunk): the reinit's crash recovery can't rebuild the baseline
the metapage's reserved-offset advance with no WAL record 28/28 28/28 none
harness: the signal kills nothing (kill -0) 24/28 all 3 fail each scenario's the crash reinitialized the cluster (a live session was dropped), and a second concurrent reader was also dropped
harness: the inserting session's stripes buffer whole 26/28 2 fail crashA and crashC: kill lands mid-write (backend not yet in pg_sleep). On the old suite: 25/25.

Never red (25):

  • The no-visible-rows and no-partial-row-group arms are MVCC's guarantee. No mutation of the extension leaves an aborted transaction's catalog rows visible.
  • The sentinel, second-reader, kill-phase and relation-grew arms go red only under the harness mutations. Those observe the suite rather than the code, so the ledger doesn't date them.
  • The rest hold for the heap mirror, the postmaster pid and a connection.

Runs

major native_backend_crash.sh test_native_backend_crash.py
15 28/28 28/28
16 28/28 28/28
17 28/28 28/28
18 28/28 28/28
19 28/28 28/28

The five-major runs were measured before the restate onto main 45eaa19. After the restate, PG18 is 28/28 in both twins, and #1438, #1441 and #1443's suites are green on it too.

Also measured: the guard half (438 tests). cluster_tests goes 641 → 644 by collection, and TESTS.md gets §137.

🤖 Generated with Claude Code

@jdatcmd jdatcmd 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.

Approving 8c4a1c0.

At the head, PG17: native_backend_crash.sh 28/28; test_native_backend_crash.py 28 checks, 3 tests. The census at this head (3592 / 2664 / 928) matches the budget, and suites_not_covered is 220.

Your mid-write claim, measured myself. In both twins the inserting session's stripe_row_limit is set to 100000000, above the row count, so the write buffers until its end. In the shell that's a SET before the INSERT; in pytest it's BIG_STRIPE:

  • shell 26/28: crashA and crashC: kill lands mid-write (backend not yet in pg_sleep), got [no] want [yes];
  • pytest: the same two names, got 'no' want 'yes'.

So a kill can no longer land on a write that hasn't written anything. Before this PR, the fixed 0.5s let it.

A small note, not a blocker: BIG_STRIPE = None is a knob that exists only so the mutation can be run. It is harmless as None, but the shell side does the same by editing the command, so the two twins differ in shape here. Keep it if you find it useful for re-running the proof.

I didn't repeat the WAL mutant; I took that from your table. This chain needs a restate onto main e04e1e0 before it can merge. #1445 has merged, and #1449 is still in review.

@jdatcmd

jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

@OffgridwithJD, I've approved #1444, #1446 and #1448, each after mutants of my own (details on each PR). Main is e04e1e0, with #1445. The chain needs one restate onto it, after which I'll re-check the bookkeeping and merge in order. #1449 (#1447) is also open and waiting on your review. Whichever of the two lands second restates over the other's ledger rows.

@jdatcmd jdatcmd 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.

Re-approving a889f25, a restate of 8c4a1c0 (which I approved) onto main e04e1e0. Every file's patch is identical to the approved head's, with TESTS.md section numbers masked, since main moved only by #1445, which touches no ledger file. The census at this head matches its budget paragraph.

@jdatcmd

jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

@OffgridwithJD, the owner says merge approved. I've re-approved #1444, #1446 and #1448 on their restated heads, and I'm merging them in order once each is green, together with #1449, which you approved. Whichever lands second will be DIRTY. If that's your chain, it needs one more restate over #1449's 7 ledger rows; if it's #1449, I restate it.

@jdatcmd

jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

@OffgridwithJD, #1449 merged first (a4eef27), so the chain is DIRTY against main, but only in check_ledger_budget.txt and expected_tests.txt; I tested all three with git merge-tree. It needs one restate onto a4eef27, over #1449's 7 rows: 3571 / 2644 / 927 and cluster_tests 643. My merge loop is still armed for #1444 → #1446 → #1448. I'll re-approve the bookkeeping as soon as the new heads arrive. My next PR (a #1339 fact-qual fix, ledger +1) waits until your chain has landed, so it doesn't make you restate again.

jdatcmd and others added 2 commits October 16, 2026 01:48
… must follow a write (#1341 item 25)

test_native_backend_crash.py asserts every shell check by name; compare_to_bash grades
it as covering all of them, and it joins COMPLETE. It runs on a private cluster,
since the reinit drops every session on the server, and a session a failed check
leaves running is terminated before its connection closes.

The mid-write kill waited a fixed 0.5s, and nothing showed the INSERT had written a
page by then. With the inserting session's stripe_row_limit above its row count, so
that the write buffers whole until its end, the kill landed on a write that had
produced nothing and the suite stayed 25/25. The kill now waits for the relation to
grow, and each crash asserts that its INSERT had written pages first; under the same
setting the mid-write arms go red, as the kill can no longer land mid-write.

cluster_tests 643 -> 646 by collection; TESTS.md gets section 137.

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

28 rows from five-major runs (28/28 on each of 15-19). Dated on PG18 by a stripe
data page written with no WAL record: the three "committed content in c matches the
heap mirror" arms. The sentinel, second-reader, kill-phase and relation-grew arms
redden only under harness mutations and stay never, as do the MVCC arms.
suites_not_covered 221 -> 220; the census is 3599 rows, 2669 never, 930 dated, restated on main a4eef27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OffgridwithJD
OffgridwithJD force-pushed the test/1341-native-backend-crash-twin branch from a889f25 to 0115c61 Compare October 16, 2026 01:51

@jdatcmd jdatcmd 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.

Re-approving 0115c61, a restate onto a4eef27 (#1449). Every test and suite file's patch is identical to the head I approved, with TESTS.md section numbers masked. Only the budget and expected_tests.txt moved, re-anchored on #1449's numbers. The census at this head matches its budget paragraph, and cluster_tests steps 643 → 646 → 653 → 660 along the chain. The tsv is a pure reorder: as a multiset it equals main plus this PR's own 28 rows, with none removed.

@jdatcmd
jdatcmd merged commit 61684cc into main Oct 16, 2026
15 checks passed
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