Skip to content

test: logical_decoding_cdc_recipe gains its pytest twin, and a rollback that undid nothing no longer passes (#1341) - #1647

Merged
jdatcmd merged 5 commits into
mainfrom
test/1341-logical-decoding-cdc-recipe-twin
Oct 22, 2026
Merged

jdatcmd merged 5 commits into
mainfrom
test/1341-logical-decoding-cdc-recipe-twin

Conversation

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

@jdatcmd The pytest twin of logical_decoding_cdc_recipe.sh (#754), every check by the shell's name, one check per test. Like the shell, it extracts the CDC recipe from docs/user-guide.md at run time and executes it, on a private cluster at wal_level=logical (pick_port(27); #1640 uses 26).

This will be restated into my stack (on top of #1641, after logical_subscriber), so its counts below are over 9d4c9416 and will move.

Holes, measured on PG17 against main's shell

hole main's shell this head (shell = twin)
The rollback arm read only after ROLLBACK. If the rolled-back INSERT never ran, "captures nothing" and "no row in the columnar table" both pass. Harness mutation: the INSERT made a duplicate key, so the block stops at it. 35/35 red by name: "inside the transaction the row was captured and is in the columnar table, before the rollback"
"and it saw the same values the table holds" used check on two readings. Harness mutation: server stopped before it. that check PASSES ("" = "") red: check_text, "a side is empty"

Also:

  • the pgc_skip name is now a literal (it embedded pg_config --pkglibdir), and the twin records the same name;
  • the seven recipe-contains premises are unrolled into literal names the grader can read (same names).

Mutations (PG17, shell and twin, SAME reds)

mutation reds
nodelete: pgcolumnar_tuple_delete marks nothing 1: the row really is gone
updnulls: the update writes its new version with every column null 1: recorded as UPDATE with the NEW value (the AFTER trigger reads the stored row)
updkeepold: the update leaves the old version live (the new version then hits the primary key) 3
nodeltrig: the guide's trigger no longer fires on DELETE 2: the DELETE capture and the decoded DELETE
updasins: the guide's capture records every non-DELETE as INSERT 4

No mutation reddens a premise. I tried a "replaying the feed reproduces the table" check and dropped it: AFTER row triggers re-read the stored row, so the feed and the table never diverged without another check already going red.

Runs and counts

  • Runs: head 36/36 in the shell and 37/37 in the twin (the 37th is the skip-name mirror) on each of PG15–19.
  • Census: 37 rows, 7 dated and 30 never. Never 3793 → 3823, dated 2073 → 2080, 5903 rows. suites_not_covered 113 → 112, cluster_tests 2454 → 2491, TESTS.md §256.
  • Gates: guards 467 passed, collection 2491, ledger gate rc=0, self-test PASSED.

🤖 Generated with Claude Code

@OffgridwithJD
OffgridwithJD force-pushed the test/1341-logical-decoding-cdc-recipe-twin branch from 57677bb to e1d08ec Compare October 21, 2026 22:48
@OffgridwithJD
OffgridwithJD force-pushed the test/1341-logical-decoding-cdc-recipe-twin branch from e1d08ec to 27b333d Compare October 21, 2026 23:19

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

Blocking, and the same defect as #1640's first head: this commit adds a ledger row named after the suite's skip message.

logical_decoding_cdc_recipe  logical_decoding_cdc_recipe  test_decoding.so is not installed; build contrib/test_decoding  15;16;17;18;19  never  -

CI has test_decoding, so the suite never emits that name, and the ledger's orphan scan fails the suites jobs, as it did on #1640. To fix: drop the row and take one from the never count in the budget. #1640's 9109eb2 is the model.

The rest holds. On PG17 at main a1b04ee, with the rolled-back INSERT made a duplicate key (id 1) in a temporary copy of each harness:

run result
main's shell 35 passed + 0 failed: the hole
this head's shell 1 failed: "inside the transaction the row was captured and is in the columnar table, before the rollback: got [ERROR: duplicate key value violates unique constraint "events_pkey" …"
this head's twin the same name, refused as a failed query

Once the row is gone, I'll approve a head whose other files are unchanged.

@OffgridwithJD
OffgridwithJD force-pushed the test/1341-logical-decoding-cdc-recipe-twin branch 2 times, most recently from 3fbdef6 to 9d5b0ad Compare October 22, 2026 04:59
@OffgridwithJD

Copy link
Copy Markdown
Collaborator Author

@jdatcmd Fixed, at 9d5b0adb, as #1640 was.

  • The row: check_ledger.tsv no longer holds the pgc_skip name "test_decoding.so is not installed; build contrib/test_decoding" (grep -c = 0). The budget paragraph now says "It enters with its 36 checks …" and "29 arrive never", and gives the reason: it's a skip, not a check, CI has test_decoding and never emits it.
  • The census: the stated never count equals the tsv's never count, and the stated rows equal the tsv's rows, at this head and at every head above it (table on Missing tests: 87 confirmed behaviours no shell or pytest arm exercises (#1339 audit) #1341).
  • The suite: test/logical_decoding_cdc_recipe.sh, test_logical_decoding_cdc_recipe.py, the compare list and unported_suites.txt are identical line for line to what you reviewed.

🤖 Generated with Claude Code

jdatcmd and others added 5 commits October 22, 2026 14:14
… guard's (#1341)

iceberg_dv.sh gains test/pytest/test_iceberg_dv.py, one check per test under the
shell's names, and two holes in the shell are closed, each measured red-first on PG17:

- "a manifest/footer offset mismatch is refused (XX001)" read the SQLSTATE alone, and
  several guards raise XX001. With the offset match removed (the !found refusal
  skipped), a later XX001 refused the same file and main's shell passed 26/26. Every
  refusal now reads its own guard's message: refusal() answers the SQLSTATE only when
  the message carries the guard's phrase, and "<SQLSTATE> (another refusal: ...)"
  otherwise.
- The three "backend still up" checks read SELECT 1, which answers again after crash
  recovery. With the referenced_data_file check removed a NULL path reaches strcmp and
  the backend dies on that arm; main passed "backend still up after the DV refusals".
  They now also read the server log's count of killed backends (crashes/survived, as
  avro_manifest), and each refusal waits for the server to answer first (await_up),
  so a crash reddens its own arm and its section's survival check, the same names in
  both harnesses, and not a timing-dependent tail of collateral.

The eight survivor comparisons move to check_text, so an empty oracle no longer
matches an empty scan.

Measured on PG17, shell then twin as shipped: seventeen mutations, each with the same
reds in both (strictgt 7, anyfile/nodup/nov2/noflags/noref/nobigoff 2, the rest 1);
26/26 in each on 15-19. 25 rows dated, 1 never (the survival check after the DV
applications, which no mutation crashed). Census 3884 -> 3885 never, 2286 -> 2311
dated, 6196 rows; suites_not_covered 102 -> 101; cluster_tests 2753 -> 2779; TESTS.md
§268.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d slot no longer passes (#1341)

The pytest twin of test/logical_decoding_source.sh (#754), one check per test,
every check by the shell's name, on a private cluster at wal_level=logical.

The shell's second slot (pgc_754b) had no heap control. Created after the
5000-row insert, it decoded nothing, and both "and it too emits no decodable
change of its own" and "100x the rows does not give 100x the internal records"
(0 < anything) passed: main 11/11. A heap table now writes through that slot in
the same window and must decode its 5000 inserts ("CONTROL: the second slot
decodes the heap table's 5000 inserts"), and the ratio check refuses a slot
that carried no internal records. Under that harness mutation both are red in
both twins (SAME 2).

The pgc_skip message for a missing test_decoding.so was built from
pg_config --pkglibdir, so the grader could not read it; it is now a literal,
and the twin records the same name (with the plugin moved away on PG17: shell
1 FAIL, twin the same 1 FAIL and 12 declined).

No source mutation reddens a check: the claim is that pgcolumnar writes nothing
logical decoding can read. A default chunk group of 100 rows still keeps the
internal records following groups (114 for 50 groups, under the 10x bound), so
it dates nothing. 12/12 shell and 13/13 twin on each of 15-19.

Census 3885 -> 3897 never, 2311 dated, 6208 rows (the skip name has no ledger row: it is not a check); suites_not_covered
101 -> 100; cluster_tests 2779 -> 2792; TESTS.md §269.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… longer passes as up (#1341)

The twin of test/iceberg_deletes.sh, one test per check under the shell's name.

Fixed in both harnesses, measured red-first against main's shell on PG17:
- The six "backend still up" checks read SELECT 1, which answers again after
  crash recovery. With abort() in place of the null-pos refusal, or the partition
  spec's fields guard removed (a NULL deref, signal 11), main passed the survival
  check once recovery had finished. They now also read the server log's count of
  backends killed by a signal, and every reading waits (bounded) for the server,
  so a crash reddens its own arm and its section's survival check rather than
  the two arms after it.
- Every refusal now also reads its own guard's message: XX001 and 22023 are each
  raised by several guards here. The oracle comparisons use check_text, so an
  empty side is not agreement.
- "the data file has 5 rows before deletes" reads pgcolumnar's own read_parquet,
  so it is not a premise (#1339).

Twenty-three mutations each redden the same names in shell and twin on PG17; head
is 49/49 in both on each of 15-19. The equal-sequence equality check is dated only
with both layered guards relaxed: the per-file strict < alone passed, because the
pre-skip at the oldest data file's sequence number drops that delete first.
Census 3897 -> 3903 never, 2311 -> 2354 dated, 6257 rows; suites_not_covered
100 -> 99; cluster_tests 2792 -> 2841; TESTS.md §270.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at applied nothing no longer passes (#1341)

The twin of test/logical_subscriber.sh, on two private clusters (a heap publisher at
wal_level=logical, a columnar subscriber), one check per test, each by the shell's name.

Four holes, each measured red-first on main's shell (PG17):
- With the insert-only subscription disabled before its UPDATE, main passed 20/20:
  "skipped by design" and "no apply errors" read after a fixed sleep. An INSERT after
  the UPDATE must now arrive before either is read.
- With no UPDATE sent to the wedge publication, "the error does NOT say only 'row
  locking'" and "the wedge is real" passed. The first now needs the replication error
  in the subscriber's log; the second reads "publisher -> subscriber".
- With every lock statement failing on a missing table, the three "is not told about
  replication" checks passed. They now need the generic row-locking error first.
- "premise: the second subscription synced before the UPDATE" is reddened by COPY
  failing in a logical worker, so it is not a premise (#1339) and is dated.
The lock loop is unrolled into literal checks so the grader reads each name.

Five mutations (hintnoname, nogate, overgate, applyins, syncins) redden the same names
in both twins; clean 20/20 on 15-19 in both, and 8/8 head repeats on 17. 20 rows,
16 dated, 4 never. Census 3903 -> 3907 never, 2354 -> 2370 dated, 6277 rows;
suites_not_covered 99 -> 98; cluster_tests 2841 -> 2861; TESTS.md §271.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck that undid nothing no longer passes (#1341)

The twin of test/logical_decoding_cdc_recipe.sh, every check by the shell's name, one
check per test, on a private cluster at wal_level=logical. Like the shell, it extracts
the CDC recipe from docs/user-guide.md at run time and executes it.

Two holes, both measured on PG17 against main's shell, fixed in both harnesses:
- The rollback arm read the capture count and the row only after ROLLBACK, so a
  rolled-back INSERT that never ran passed both arms. With it made a duplicate key, so
  the block stopped there, main passed 35/35. A reading inside the transaction, before
  the rollback, now requires the row both captured and present ("1:1"). Under the same
  mutation it is red in shell and twin.
- "and it saw the same values the table holds" compared two readings with check. With
  the server stopped before it, "" = "" passed. It is check_text now.
The missing-plugin pgc_skip name is a literal the twin records, and the seven
recipe-contains premises are unrolled into literal names the grader can read.

Five mutations date seven checks, the same names red in both twins on PG17:
pgcolumnar_tuple_delete marking nothing (1), the update writing its new version with
every column null (1), the update leaving the old version live (3), and two mutations
of the guide's recipe: the trigger not firing on DELETE (2), and every non-DELETE
captured as an INSERT (4). 36/36 shell and 37/37 twin on each of 15-19. Census 3907 ->
3936 never, 2370 -> 2377 dated, 6313 rows (the skip name has no ledger row: it is not a check); suites_not_covered 98 -> 97;
cluster_tests 2861 -> 2898; TESTS.md §272.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OffgridwithJD
OffgridwithJD force-pushed the test/1341-logical-decoding-cdc-recipe-twin branch from 9d5b0ad to 4d3ca68 Compare October 22, 2026 14:33

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

Approved at 4d3ca68: the skip-name row is gone, and against 9d5b0ad (which I verified) every content file is unchanged; census agrees with the tsv.

@jdatcmd
jdatcmd merged commit fa44ce2 into main Oct 22, 2026
13 of 14 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