Skip to content

test: am_contracts' older-snapshot arm waits for its events instead of sleeping - #1417

Merged
jdatcmd merged 3 commits into
mainfrom
test/am-contracts-wait-for-index
Oct 15, 2026
Merged

jdatcmd merged 3 commits into
mainfrom
test/am-contracts-wait-for-index

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Fixes a timing-dependent arm in test/am_contracts.sh, added by #1364. Test-only.

The flake

The older-snapshot arm held a REPEATABLE READ snapshot, slept four seconds, and assumed the DELETE and CREATE INDEX would finish inside that window. On a loaded box they did not. The plan was taken before the index existed, and premise: the older snapshot's lookup is an index scan read 0. This happened on PG16 during a local run of #1413, and a rerun passed. The real arm after it still passed, because a seqscan also finds the row. So the premise was the only thing standing between the arm and a vacuous pass.

The fix

The steps are now ordered by events, not sleeps:

  1. A gate session takes an advisory lock and names itself.
  2. Session 1 takes its snapshot, names itself, and queues for that lock.
  3. The script waits for that name, then runs the DELETE and CREATE INDEX.
  4. The script ends the gate session. Session 1 then plans, after the index exists.

A new premise in both twins says that the older snapshot counted all 10000 rows, so it predates the delete.

Evidence (PG17 unless stated)

run result
old arm, 5 s delay before CREATE INDEX 1 failed: premise: the older snapshot's lookup is an index scan: got [0]
new arm, no delay 28/28
new arm, same 5 s delay 28/28
new arm, index build skips deleted rows again (mutant) 1 failed: an index built after a delete finds the row …: got [0] want [1]
new arm, PG15–19 28/28 each, twin 7/7 each

Ledger: one premise row, never dated (#1236): 2774 + 1 = 2775 rows, 2096 never. The gate passes against origin/main. The harness selftest passes 1245/1245 and the guard set 433/433.

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

Approved at 8bf3cbf.

  • The fix: the older-snapshot arm is now ordered by events. A gate session takes advisory lock 4242 and only then names itself. Session 1 takes its REPEATABLE READ snapshot, names itself and queues on the lock. The script waits for that name, runs the DELETE and CREATE INDEX, then ends the gate. A new premise checks that the snapshot counted 10000 rows.
  • Red (PG17), the stated mutant: the index build with PgColumnarReadSetEmitDeleted(readState, false) (.so b9726239 -> ca727fa0). Shell 27 + 1: an index built after a delete finds the row for an older snapshot that sees it: got [0] want [1]. Twin 1 failed, 6 passed, same text.
  • The wait can't pass vacuously, and it fails when the event never comes:
    • With SET application_name = 'ib_snap' removed, the loop times out after 30s and the run is 27 + 1: premise: the older snapshot was taken before the delete: got [no (gate 891671, snapshot none)] want [10000].
    • With session 1's pg_advisory_lock_shared(4242) replaced by SELECT 1, the run is 26 + 2, including premise: the older snapshot's lookup is an index scan: got [0] want [1]. So the gate is what makes the plan come after the index.
    • With sleep 5 added before CREATE INDEX, the run is 28/28.
  • Green: shell 28/28 and twin 7 passed on PG17 and PG18. The ledger, 2775 rows / 2096 never / 679 dated, matches the tsv, the gate agrees against origin/main, and the new name has its row.

🤖 Generated with Claude Code

jdatcmd and others added 3 commits October 15, 2026 16:36
…1341 item 50)

Two finite float8s, or two finite reals, whose sum is infinite raise
"value out of range: overflow" (22003) in core's float8pl and float4pl.
The grouped node's accumulators carry the same check, and nothing tested
it: only avg's overflow had an arm, in the shell alone. native_groupagg
gains the sum arm in both twins, judged by SQLSTATE against core with the
node off, plus a check that the node fires. Removing either accumulator's
check reddens its own position.

native_groupagg enters the ledger seeded; suites_not_covered 234 -> 233.
cluster_tests +1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
…#1407)

older snapshot still finds a recently deleted row by a new index. But it
emitted every deleted row, where heap's build skips a tuple dead to all.
So REINDEX, documented as what reclaims stale entries, reclaimed none:
9 rows in 10 deleted, and the rebuilt index kept its size where heap's
shrank to a tenth.

The build now also reads each row group's delete marks under a horizon
snapshot (xmin = xmax = the oldest non-removable xid, curcid 0), which
sees exactly the transactions committed before every running snapshot
began, and leaves out a row deleted there. The snapshot is registered:
PG 18+ asserts it in the visibility check.

am_contracts gains the arm in both twins, red on main (kept:2260992->
2260992); with the horizon moved to the next xid, the older-snapshot arm
goes red instead. limitations.md says when REINDEX keeps an entry.
cluster_tests +1.

Fixes #1407.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
…f sleeping

The arm from #1364 held a REPEATABLE READ snapshot, slept four seconds,
and assumed the DELETE and CREATE INDEX finished inside them. On a loaded
box they did not: the plan was taken before the index existed, and
'premise: the older snapshot's lookup is an index scan' read 0 on PG16.
A 5-second delay before CREATE INDEX reproduces it on PG17 every time.

Now a gate session holds an advisory lock. Session 1 takes its snapshot,
names itself, and queues for the lock. The script waits for the name,
deletes, builds the index, then ends the gate. With the same delay the
suite passes 28/28. A new premise in both twins says the older snapshot
counted all 10000 rows, so it predates the delete. The arm still goes red
when the index build skips deleted rows again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
@jdatcmd
jdatcmd force-pushed the test/am-contracts-wait-for-index branch from 8bf3cbf to 7aa53fa Compare October 15, 2026 16:39

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

Re-approved at 7aa53fa, stacked on #1412.

  • Same patch: this PR's own commit (e8a1af5..7aa53fa) matches what I approved at 8bf3cbf, outside the count files and ledger rows included.
  • Census: 2969 / 2273 / 696 from the tsv, which matches the paragraph.
  • CI: 13 checks green and 2 still running at the time of review.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit 5e3e914 into main Oct 15, 2026
15 checks passed
This was referenced Oct 15, 2026
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