diff --git a/CHANGELOG.md b/CHANGELOG.md index ab231dfd..74ce1f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,54 @@ true until the next version shipped. KNOWN LIMIT, stated because it changes what a green CI check means here: at depth 1 with no tags every section arm is skipped, so CI cannot run any of this. It runs locally and in the five-major release gate, which is where a release is cut. +- Eleven timeout paths printed a FAIL that nothing recorded, and the accounting + balanced at the wrong number (#965). + + `concurrency.sh`, `unique_conc.sh` and `update_conc.sh` bound every wait. On a + timeout each printed `FAIL timeout waiting for ...`, set the suite-local `fail` + and returned -- touching neither `PGC_CHECKS` nor the record stream. The suite + still exited 1, so this was never a false green. What it was is worse than a + missing number: + + main, with a timeout induced rc=1, CONCURRENCY TEST FAILED + human FAIL lines 1 + RESULT records with FAIL 0 + RESULT records total 7 + checks run: 7 + + Records and total agree, so nothing refuses the log -- on a run that printed a + FAIL and exited 1. A missing figure can be noticed; one that reconciles cannot. + + Each path now records through `pgc_record`. The same induced timeout: + + this change rc=1, CONCURRENCY TEST FAILED + RESULT records with FAIL 2 + RESULT records total 9 + checks run: 9 + + THE NAME IS FIXED PER WAIT KIND, with the session and sentinel in the REASON + field. The ledger is keyed on (suite, part, name), so interpolating + `"$name/$label"` would mint rows nobody can enumerate and therefore nobody can + seed. Four kinds: a command's sentinel, a standalone sentinel, a session + blocking, a session reaching idle-in-transaction. + + It cannot be proved by running the suite green -- not one of these lines + executes on a green run, so every count is identical whether the conversion is + right, wrong or absent. Proved by lowering all four wait bounds from 1200 to 1 + in a scratch tree, with the rewritten count asserted before the run, and with + unmodified `main` as the control. Green runs are unchanged: 7 records and + `checks run: 7` before and after. + +- Every record these three suites emitted named `major=unknown` (#965). + + `pgc_record` reads `${PGC_MAJOR:-unknown}`, and `PGC_MAJOR` is set by + `pgc_setup`, which these three do not call -- they carry their own harness. + Measured on a green run before the fix: 7 of 7 records said `unknown`. A ledger + row claiming to hold on `unknown` matches no run, so none of these checks could + ever have been seeded, which is part of why `suites_not_covered` has a floor + here. One line each, from the `PG_CONFIG` they already resolve. All records now + carry the real major: `concurrency` 7, `unique_conc` 31, `update_conc` 25, every + one of them `18` on PG18, each reconciling with its own `checks run:`. - Four secret-leak claims over the PG server log could pass having read nothing (#1032). diff --git a/test/concurrency.sh b/test/concurrency.sh index 51f79cd6..695486b7 100755 --- a/test/concurrency.sh +++ b/test/concurrency.sh @@ -52,10 +52,39 @@ # assignments and function definitions only, so sourcing it starts nothing. . "$(dirname "${BASH_SOURCE[0]}")/lib.sh" +# A TIMEOUT IS A CHECK RESULT (#965). These paths printed `FAIL timeout waiting +# for ...`, set the suite-local `fail` and returned -- touching neither PGC_CHECKS +# nor the record stream. A failing run then reconciled as `N passed + 0 failed = +# N`: an aggregate that BALANCES while asserting zero failures on a run that +# failed. That is worse than invisibility, because a missing number can be noticed +# and a balancing one cannot. Induced and measured rather than argued. +# +# THE NAME IS FIXED PER WAIT KIND, with the session and sentinel in pgc_record's +# REASON field. The ledger is keyed on (suite, part, name), so interpolating +# "$name/$label" into the name would mint rows nobody can enumerate and therefore +# nobody can seed. +# +# NO GREEN RUN EXECUTES THIS. It records only on the timeout path, so a passing +# suite emits nothing here and the record stream is unchanged. +wait_timeout() { # wait_timeout FIXED-NAME DETAIL + pgc_record FAIL "$1" "FAIL $1 (timed out waiting for $2)" "$2" + fail=1 +} + + set -uo pipefail PG_CONFIG="${1:-/usr/local/pg17/bin/pg_config}" BINDIR="$("$PG_CONFIG" --bindir)" + +# EVERY RECORD THIS SUITE EMITS CARRIED `major=unknown` (#965). `pgc_record` reads +# `${PGC_MAJOR:-unknown}`, and PGC_MAJOR is set by `pgc_setup` -- which this suite +# does not call, because it carries its own harness. So all of its rows named a +# major that is not a major, and a ledger keyed on (suite, part, name, majors) +# cannot seed them: the row would claim to hold on "unknown" and match no run. +# Measured before the fix, on a green run: 7 of 7 records said `unknown`. +PGC_MAJOR="$(pgc_major_of "$PG_CONFIG")" + SRCDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WORKDIR="$(mktemp -d /tmp/pgcolumnar-conc.XXXXXX)" @@ -239,8 +268,7 @@ send_wait() { # name label sql... while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label" - fail=1 + wait_timeout "a bounded wait for a command's sentinel completed" "$name/$label" return 1 fi done @@ -254,8 +282,7 @@ wait_sentinel() { # name label while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label sentinel" - fail=1 + wait_timeout "a bounded wait for a standalone sentinel completed" "$name/$label" return 1 fi done @@ -270,8 +297,7 @@ wait_blocked() { # application_name [ "$n" = "1" ] && return 0 sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $app to block" - fail=1 + wait_timeout "a bounded wait for a session to block completed" "$app" return 1 fi done @@ -285,8 +311,7 @@ wait_idle_intx() { # application_name [ "$st" = "idle in transaction" ] && return 0 sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $app to go idle-in-transaction" - fail=1 + wait_timeout "a bounded wait for a session to reach idle-in-transaction completed" "$app" return 1 fi done diff --git a/test/unique_conc.sh b/test/unique_conc.sh index af93dc5a..7b29eb55 100755 --- a/test/unique_conc.sh +++ b/test/unique_conc.sh @@ -59,10 +59,39 @@ # assignments and function definitions only, so sourcing it starts nothing. . "$(dirname "${BASH_SOURCE[0]}")/lib.sh" +# A TIMEOUT IS A CHECK RESULT (#965). These paths printed `FAIL timeout waiting +# for ...`, set the suite-local `fail` and returned -- touching neither PGC_CHECKS +# nor the record stream. A failing run then reconciled as `N passed + 0 failed = +# N`: an aggregate that BALANCES while asserting zero failures on a run that +# failed. That is worse than invisibility, because a missing number can be noticed +# and a balancing one cannot. Induced and measured rather than argued. +# +# THE NAME IS FIXED PER WAIT KIND, with the session and sentinel in pgc_record's +# REASON field. The ledger is keyed on (suite, part, name), so interpolating +# "$name/$label" into the name would mint rows nobody can enumerate and therefore +# nobody can seed. +# +# NO GREEN RUN EXECUTES THIS. It records only on the timeout path, so a passing +# suite emits nothing here and the record stream is unchanged. +wait_timeout() { # wait_timeout FIXED-NAME DETAIL + pgc_record FAIL "$1" "FAIL $1 (timed out waiting for $2)" "$2" + fail=1 +} + + set -uo pipefail PG_CONFIG="${1:-/usr/local/pg17/bin/pg_config}" BINDIR="$("$PG_CONFIG" --bindir)" + +# EVERY RECORD THIS SUITE EMITS CARRIED `major=unknown` (#965). `pgc_record` reads +# `${PGC_MAJOR:-unknown}`, and PGC_MAJOR is set by `pgc_setup` -- which this suite +# does not call, because it carries its own harness. So all of its rows named a +# major that is not a major, and a ledger keyed on (suite, part, name, majors) +# cannot seed them: the row would claim to hold on "unknown" and match no run. +# Measured before the fix, on a green run: 7 of 7 records said `unknown`. +PGC_MAJOR="$(pgc_major_of "$PG_CONFIG")" + SRCDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PG_MAJOR="$("$PG_CONFIG" --version | sed -E 's/^[^0-9]*([0-9]+).*/\1/')" @@ -215,7 +244,7 @@ send_wait() { # name label sql... while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label"; fail=1; return 1 + wait_timeout "a bounded wait for a command's sentinel completed" "$name/$label"; return 1 fi done return 0 @@ -226,7 +255,7 @@ wait_sentinel() { # name label while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label sentinel"; fail=1; return 1 + wait_timeout "a bounded wait for a standalone sentinel completed" "$name/$label"; return 1 fi done return 0 @@ -238,7 +267,7 @@ wait_blocked() { # application_name [ "$n" = "1" ] && return 0 sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $app to block"; fail=1; return 1 + wait_timeout "a bounded wait for a session to block completed" "$app"; return 1 fi done } @@ -249,7 +278,7 @@ wait_idle_intx() { # application_name [ "$st" = "idle in transaction" ] && return 0 sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $app idle-in-transaction"; fail=1; return 1 + wait_timeout "a bounded wait for a session to reach idle-in-transaction completed" "$app"; return 1 fi done } diff --git a/test/update_conc.sh b/test/update_conc.sh index 201bd05a..9ce45ce8 100755 --- a/test/update_conc.sh +++ b/test/update_conc.sh @@ -68,10 +68,39 @@ # assignments and function definitions only, so sourcing it starts nothing. . "$(dirname "${BASH_SOURCE[0]}")/lib.sh" +# A TIMEOUT IS A CHECK RESULT (#965). These paths printed `FAIL timeout waiting +# for ...`, set the suite-local `fail` and returned -- touching neither PGC_CHECKS +# nor the record stream. A failing run then reconciled as `N passed + 0 failed = +# N`: an aggregate that BALANCES while asserting zero failures on a run that +# failed. That is worse than invisibility, because a missing number can be noticed +# and a balancing one cannot. Induced and measured rather than argued. +# +# THE NAME IS FIXED PER WAIT KIND, with the session and sentinel in pgc_record's +# REASON field. The ledger is keyed on (suite, part, name), so interpolating +# "$name/$label" into the name would mint rows nobody can enumerate and therefore +# nobody can seed. +# +# NO GREEN RUN EXECUTES THIS. It records only on the timeout path, so a passing +# suite emits nothing here and the record stream is unchanged. +wait_timeout() { # wait_timeout FIXED-NAME DETAIL + pgc_record FAIL "$1" "FAIL $1 (timed out waiting for $2)" "$2" + fail=1 +} + + set -uo pipefail PG_CONFIG="${1:-/usr/local/pg17/bin/pg_config}" BINDIR="$("$PG_CONFIG" --bindir)" + +# EVERY RECORD THIS SUITE EMITS CARRIED `major=unknown` (#965). `pgc_record` reads +# `${PGC_MAJOR:-unknown}`, and PGC_MAJOR is set by `pgc_setup` -- which this suite +# does not call, because it carries its own harness. So all of its rows named a +# major that is not a major, and a ledger keyed on (suite, part, name, majors) +# cannot seed them: the row would claim to hold on "unknown" and match no run. +# Measured before the fix, on a green run: 7 of 7 records said `unknown`. +PGC_MAJOR="$(pgc_major_of "$PG_CONFIG")" + SRCDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WORKDIR="$(mktemp -d /tmp/pgcolumnar-upconc.XXXXXX)" @@ -213,7 +242,7 @@ send_wait() { # name label sql... while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label"; fail=1; return 1 + wait_timeout "a bounded wait for a command's sentinel completed" "$name/$label"; return 1 fi done return 0 @@ -224,7 +253,7 @@ wait_sentinel() { # name label while ! grep -q "<<$label>>" "$outfile" 2>/dev/null; do sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $name/$label sentinel"; fail=1; return 1 + wait_timeout "a bounded wait for a standalone sentinel completed" "$name/$label"; return 1 fi done return 0 @@ -236,7 +265,7 @@ wait_blocked() { # application_name [ "$n" = "1" ] && return 0 sleep 0.05; i=$((i + 1)) if [ "$i" -ge 1200 ]; then - echo "FAIL timeout waiting for $app to block"; fail=1; return 1 + wait_timeout "a bounded wait for a session to block completed" "$app"; return 1 fi done }