Repository navigation
test: corruption gets its pytest twin, and a backend crash can no longer pass for a refusal (#1341 item 25) - #1446
Conversation
|
Also measured at this head, which contains #1444: harness_selftest 1245/1245 and docs_style PASSED, on PG18. 🤖 Generated with Claude Code |
jdatcmd
left a comment
There was a problem hiding this comment.
Approving 8a4d96e.
At the head, PG17: corruption.sh 20/20; test_corruption.py 20 checks, 7 tests. The census at this head (3612 / 2673 / 939) matches the budget, and suites_not_covered is 219.
Your two central claims, with mutants of my own:
| mutant | shell | pytest |
|---|---|---|
the "unrecognized native encoding descriptor" refusal → abort() |
18/20: bad descriptor errors, got [no error (server closed the connection unexpectedly)]; alive after bad descriptor, got [no (1 backend crashes)] |
bad descriptor errors, no error (consuming input failed: …) |
both malformed-header returns in PgColumnarBloomProbe read as "absent" (return false) |
19/20: corrupt bloom: a value the table holds is still found, got [0] want [1] |
the same arm, got 0 want 1 |
So a crash can no longer pass for a refusal, and a corrupt filter that loses rows is now caught. I restored the source with a make clean, and git status was clean.
As in the earlier twins, not a blocker: pytest stops at the first failed expect, so under the abort mutant it never reaches alive (19 checks run). The shell records both.
I didn't repeat your other six mutants; I took them from your table.
8a4d96e to
81f0901
Compare
… 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>
…ger pass for a refusal (#1341 item 25) test_corruption.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 a crash drops every session. Three kinds of arm could not fail as claimed: - `errors` said yes to any failure of psql, and a crash is one; `alive` connected after crash recovery and passed whenever recovery won the race. With the bad-descriptor refusal replaced by abort() and `alive` 3s later, the suite was 12/12. A refusal must now be a clean XX001, and `alive` also requires that no backend died since the scenario began, read from the server log. - each corrupting UPDATE was assumed to change a row. With every UPDATE matching nothing, the value_count, column_index and bloom scenarios passed on tables nobody corrupted. Each now asserts that it changed a catalog row. - the bloom arm looked up an absent value, which is 0 rows even when a corrupt filter wrongly says "absent". With a malformed header read as "absent", the suite was 12/12; a value the table holds must now still be found. cluster_tests 646 -> 653 by collection; TESTS.md gets section 138. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions of the reader and bloom probe (#1341) 20 rows from five-major runs (20/20 on each of 15-19). Dated on PG18 by: the group- span check removed (1); the span (2), chunk-bound (3) and descriptor (2) refusals each replaced by a crash; the row accounting and value-stream bound removed (2); a malformed bloom header read as absent (1); the bloom length check removed (2). suites_not_covered 220 -> 219; the census is 3619 rows, 2678 never, 941 dated, restated on main a4eef27. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81f0901 to
ea88468
Compare
jdatcmd
left a comment
There was a problem hiding this comment.
Re-approving ea88468, 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.
#1341 item 25, its third and last suite: a corrupt catalog row is refused cleanly, or degrades safely, and no backend dies.
corruption.shhad no pytest twin. This PR adds one, every check by the shell's name, and seeds the suite into the ledger. The port found three kinds of arm in the shell that couldn't fail as claimed, and they are fixed in both twins.Stacked on #1444. Merge #1444 first; this branch is #1444's head plus two commits.
What changes
test/pytest/test_corruption.py, the twin. It has seven tests, one per corrupted field, on a private cluster, since a crash drops every session.compare_to_bash.pygrades it as covering every shell name, and it joinsCOMPLETE.errorssaid yes to any failure of psql, and a crash is one.aliveconnected after the postmaster's crash recovery, so it passed whenever recovery finished first.abort(), the old suite caught the crash only becausealivereached the server mid-recovery. Withalive3s later, it was 12/12.XX001, read from psql'sVERBOSITY verboseoutput, or fromexc.sqlstatein the twin. A crash prints no SQLSTATE.alivealso requires that no backend has died since the scenario began, counted from "terminated by signal" in the server log, which doesn't depend on timing.premise: the <field> corruption changed a catalog row.corrupt bloom: a value the table holds is still found.corruptionis seeded with 20 rows from five-major runs, and 11 of the 20 rows dated by the mutants below.suites_not_coveredgoes 220 → 219, and the census is 3592 + 20 = 3612 rows, 2664 + 9 = 2673 never and 928 + 11 = 939 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
.somd5 printed every time.corruption.shtest_corruption.pygot [no error (20000|…)] want [XX001]abort()no error (server closed the connection unexpectedly),no (1 backend crashes)abort()(both sites)abort()corrupt bloom: a value the table holds is still found:got [0] want [1](old suite: 12/12)Each one stays green alone:
Each of these corruptions meets a second guard that still refuses it cleanly. That's why the refusal →
abort()mutants are the ones that test the arms: they replace the refusal that actually fires.Harness mutations:
alive3s after the abort mutant: still red, where the old suite was 12/12.Never red (9):
Runs
corruption.shtest_corruption.pyAlso measured: the guard half (438 tests).
cluster_testsgoes 644 → 651 by collection, and TESTS.md gets §138.🤖 Generated with Claude Code