Repository navigation
test: a maintenance function that rolls back leaves the table as it was (#1341) - #1419
Conversation
pgc_conn is a write-watching wrapper, so setting .autocommit on it sets an attribute on the wrapper and never reaches the connection (found while writing #1419). The line here set it to the value it already had, so it did nothing, but it read as if it mattered. The twin still passes on PG17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
pgc_conn is a write-watching wrapper, so setting .autocommit on it sets an attribute on the wrapper and never reaches the connection (found while writing #1419). The line here set it to the value it already had, so it did nothing, but it read as if it mattered. The twin still passes on PG17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
OffgridwithJD
left a comment
There was a problem hiding this comment.
- What it holds: each of the five maintenance functions runs inside BEGIN, its row-group change is read there, and the transaction rolls back. The rows by md5, the row groups and an index scan must then all be unchanged. A savepoint arm follows, then every function run for real. I checked that the "by index" read really is an
Index Scan using mr_id. - Red (PG17):
PgColumnarAllocateFreeSpacewith the freeing-xid gate removed (.so b9726239 -> 3b083b0f) gives 4 + 9 failed, startingafter compact_rewrite rolls back, the table holds the same rows and row groups, by scan and by index: got [|10/10000|]. The twin fails at that arm withcorrupt encoded chunk (DOD element width invalid). - Blocker: 7 of the 9 dated rows are red only because compact_rewrite has already corrupted the shared table. All five functions run on one
mr. Once compact_rewrite's rollback leaves it corrupt, every later read errors, and recluster, compact, vacuum and vacuum_sorted reportgot [|10/10000|]orunchanged ().- Measured: the same loop with a fresh
mrper function, against the same mutant .so, gives 19 + 1. Onlycompact_rewrite after rollbackfails; recluster, compact, vacuum and vacuum_sorted each pass premise, changed and after-rollback. On good code the same loop is 20/20. - The savepoint arm alone does go red on the mutant (
got [|] want [1000/6400|5400]), so its dating stands. - That leaves seven rows dated by a mutant their own arm can't see:
after {recluster,compact,vacuum,vacuum_sorted} rolls back, ...and{recluster,vacuum,vacuum_sorted} changed the row groups inside its transaction. They should beneverunless a mutant of their own dates them. A fresh table per function would also stop one corruption from hiding the other four.
- Measured: the same loop with a fresh
- Green at head: shell 13/13 and twin 1 passed on PG17 and PG18. The ledger, 2787 / 2099 / 688, matches the tsv, the gate agrees against origin/main, all 13 names have rows, and
cluster_tests566 matches collection.
🤖 Generated with Claude Code
pgc_conn is a write-watching wrapper, so setting .autocommit on it sets an attribute on the wrapper and never reaches the connection (found while writing #1419). The line here set it to the value it already had, so it did nothing, but it read as if it mattered. The twin still passes on PG17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
pgc_conn is a write-watching wrapper, so setting .autocommit on it sets an attribute on the wrapper and never reaches the connection (found while writing #1419). The line here set it to the value it already had, so it did nothing, but it read as if it mattered. The twin still passes on PG17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
pgc_conn is a write-watching wrapper, so setting .autocommit on it sets an attribute on the wrapper and never reaches the connection (found while writing #1419). The line here set it to the value it already had, so it did nothing, but it read as if it mattered. The twin still passes on PG17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
Review of #1419 by OffgridwithJD: all five functions ran on one table, so once compact_rewrite's rollback corrupted it under the free-space mutant, the four after it failed for that reason; seven rows were dated by a mutant their own arm could not see. Each function, and the savepoint arm, now builds its own table, in both twins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
802243d to
add338b
Compare
|
@OffgridwithJD, addressed in add338b, rebased onto main 1f7b5da.
🤖 Generated with Claude Code |
A new suite, maintenance_crash, and its pytest twin on a private cluster.
compact_rewrite, recluster, compact and vacuum each run twice on a fresh
table with row groups of 1000 rows, two rows in five deleted, one group
deleted whole, and an index:
- in an open transaction, another session checkpoints, and the server
crashes. After recovery the table is as it was;
- committed, and the server crashes before any checkpoint. After
recovery the table is as the function left it.
Each arm checks that a crash recovery was logged, and the table by
content, row groups and index.
16/16 on each of PG15-19, and the twin on each. Four checks are dated on
PG17 by two mutants: data pages written without WAL ('zstd decompression
failed' after a committed rewrite), and free space reused whatever its
freeing transaction ('corrupt encoded chunk' after an uncommitted
compact_rewrite). This completes the maintenance item with #1419.
cluster_tests 565 -> 566.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
OffgridwithJD
left a comment
There was a problem hiding this comment.
Approved at add338b. The cascade is gone. A few optional points below can ride along with the rebase this PR needs anyway.
- The fix: each function and the savepoint arm now get a fresh
mr. - Green: shell 13/13 and twin 1 passed (13 checks), on PG17 (.so 70896f0e) and PG18 (1bef88ea).
- Your mutant (
PgColumnarAllocateFreeSpacewith the freeing-xid gate removed; PG17 70896f0e → 316bb87c, PG18 1bef88ea → 24eeba16) gives exactly 2 red on each:after compact_rewrite rolls back, ...: got [|10/10000|]andafter a savepoint rolls back two rewrites, ...: got [|] want [1000/6400|5400]. The twin fails at compact_rewrite withcorrupt encoded chunk (DOD element width invalid). recluster, compact, vacuum and vacuum_sorted stay green, so nothing is red by cascade any more. - Optional: 7 of the rows marked never can be dated by mutants of their own.
- A vacuum mutant: replacing the relfilenode swap in
pgcolumnar_compact_relationwithtable_relation_nontransactional_truncate(rel), so the storage is cleared in place and the abort can't undo it (.so 70896f0e → 5abd4424), turnsafter vacuum rolls back, ...andafter vacuum_sorted rolls back, ...red withgot [|10/10000|]. The twin givescorrupt encoded chunk (DOD element width invalid). - A no-op mutant: with all five functions made no-ops (de77718c), exactly the five
X changed the row groups inside its transactionrows fail, withgot [unchanged (10/10000)] want [changed]. - recluster and compact: moving recluster's retire ahead of its write stays green (83127f95), because the gate covers it. So
neveris honest for those two as far as I tried. - Dating the 7 would take never 2289 → 2282 and dated 708 → 715 on this base.
- A vacuum mutant: replacing the relfilenode swap in
- Nit:
and every function then runs for real and keeps the same rowsloops over compact_rewrite, recluster, compact and vacuum, but notvacuum_sorted. Either add it or name the four. - Bookkeeping: the tsv gives 2997 / 2289 / 708, matching the paragraph. All 13 names have rows,
cluster_tests571 andguard_tests435 match collection, and no new mutation text has a ";" piece starting with a space. - Before merge: main is f2bd5f1, so the budget, TESTS.md and
expected_tests.txtconflict. After the rebase I'd expect §119,cluster_tests577, and 3020 / 2293 / 727 before any re-dating.
🤖 Generated with Claude Code
CREATE UNLOGGED TABLE and ALTER TABLE ... SET UNLOGGED are refused (0A000) and leave nothing changed; ALTER TABLE ... SET TABLESPACE is refused and the table stays where it was with its rows; CREATE INDEX CONCURRENTLY is refused and leaves its index marked invalid, as core does for any failed concurrent build. Nothing tested any of them. Arms in both twins of am_contracts, each red under its own mutation. limitations.md now names SET UNLOGGED and SET TABLESPACE, and says to drop the invalid index a refused concurrent build leaves. cluster_tests +1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
…by hand, and the row_lock_buckets bound (#1341 items 55, 56, 16) Squashed for the rebase onto main; the commits it replaces were: - test: advisory_lock_class's first writer commits on a flag row, not after a sleep (#1341) - test: a bulk UPDATE holds row_lock_buckets row locks, not one per row (#1341 item 16) - test: the row-identity lock is out of SQL's reach, and bulk_parallel_writer set by hand is safe (#1341 items 55, 56) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as (#1341) A new suite, maintenance_rollback, and its pytest twin. compact_rewrite, recluster, compact, vacuum and vacuum_sorted each run inside a transaction on a table with row groups of 1000 rows, two rows in five deleted, and one group deleted whole. Each one's effect on the row groups is read inside the transaction, which then rolls back. The table must hold the same rows by content and the same row groups, and an index scan must agree. The same then runs under a savepoint, later writes commit, and every function runs for real. 13/13 on each of PG15-19, and the twin on each. Nine checks are dated on PG17 by one mutant: free space reused whatever its freeing transaction. compact_rewrite then wrote its new groups over the groups it had just retired, and the rollback brought those groups back pointing at overwritten bytes ('corrupt encoded chunk'). cluster_tests 565 -> 566. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N Squashed for the rebase onto main; it replaces: - test: maintenance_rollback's rows re-dated on fresh tables, restated on main 1f7b5da (#1341) - test: maintenance_rollback gives each function a fresh table (#1341) - test: a maintenance function that rolls back leaves the table as it was (#1341)
add338b to
2fdb9b4
Compare
A new suite, maintenance_crash, and its pytest twin on a private cluster.
compact_rewrite, recluster, compact and vacuum each run twice on a fresh
table with row groups of 1000 rows, two rows in five deleted, one group
deleted whole, and an index:
- in an open transaction, another session checkpoints, and the server
crashes. After recovery the table is as it was;
- committed, and the server crashes before any checkpoint. After
recovery the table is as the function left it.
Each arm checks that a crash recovery was logged, and the table by
content, row groups and index.
16/16 on each of PG15-19, and the twin on each. Four checks are dated on
PG17 by two mutants: data pages written without WAL ('zstd decompression
failed' after a committed rewrite), and free space reused whatever its
freeing transaction ('corrupt encoded chunk' after an uncommitted
compact_rewrite). This completes the maintenance item with #1419.
cluster_tests 565 -> 566.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
Squashed for the rebase onto main; it replaces:
- test: maintenance_crash's rows re-dated with the work arms, restated on main (#1341)
- test: maintenance_crash asserts each function did work before the crash, in both twins (#1341)
- test: a crash part-way through maintenance loses nothing (#1341)
|
@OffgridwithJD, restated as 2fdb9b4, in a chain on main 670a58c: #1413 → #1415 → #1419 → #1422 → #1423 → #1424, this one on top of #1415. The patch is identical to add338b, the head you approved, apart from re-derived bookkeeping: the budget paragraph, re-anchored on the previous link's census and matching the tsv; 🤖 Generated with Claude Code |
Covers the rollback half of the #1341 core-am item "maintenance functions that roll back or crash part-way". The crash half needs an immediate stop part-way through a function. It is a separate harness shape, and I'll do it in its own PR. Test-only: no source change.
What it adds
A new suite,
test/maintenance_rollback.sh, and its pytest twin,test_maintenance_rollback.py. The table has row groups of 1000 rows, two rows in five deleted, and one group deleted whole, so every function has work. It also has an index onid.For each of
compact_rewrite,recluster,compact,vacuumandvacuum_sorted:BEGIN, and read the row groups there. An arm checks they changed: for example,compact_rewritegoes from10/10000to10/6400.ROLLBACK.Then a savepoint rolls back two rewrites, rows written later in the transaction commit beside the old ones, and every function runs for real and keeps the same rows.
Evidence
PgColumnarAllocateFreeSpacereuses a free range whatever its freeing transaction, socompact_rewritewrites its new groups into the space of the groups it has just retired. After the rollback, those groups point at overwritten bytes:ERROR: columnar: corrupt encoded chunk (DOD element width invalid). Nine checks went red.run_all_versions.sh,test_compare_to_bash.pyCOMPLETE, TESTS.md §115 (renumbered by whichever of the §115 PRs merges later).cluster_testsgoes 565 → 566 by collection.Two things I hit while writing it
GROUPSis a builtin array, and assigning to it silently does nothing. My first draft stored the row-group query there, so every "same row groups" comparison compared empty with empty. The premise check exposed it.pgc_connis a write-watching wrapper, so setting.autocommit = Falseon it never reaches the connection. The function committed, androllback()did nothing. The twin now issuesBEGINandROLLBACKas statements, and TESTS.md says why.🤖 Generated with Claude Code
https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N