Skip to content

Add periodic assert-build CI pass - #63

Merged
matt-welch merged 2 commits into
mainfrom
assert-build-ci-pass
Oct 8, 2026
Merged

matt-welch merged 2 commits into
mainfrom
assert-build-ci-pass

Conversation

@matt-welch

@matt-welch matt-welch commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a periodic, non-blocking CI pass that builds PostgreSQL with --enable-cassert and runs this extension's full regression and TAP suites against it, so invariant violations that Assert() catches (but a release build would silently tolerate or crash differently on) get caught somewhere. The existing build-and-test workflow never builds with assertions enabled, so this is new coverage, not a change to any existing job. Two files are added: .github/workflows/assert-build.yml (the workflow itself, triggered only by schedule and workflow_dispatch, never pull_request or push, and not a required status check) and ci/classify_assert_results.sh (used by that workflow to tell apart one specific, already-known, already-accepted assertion trap from anything else). The known trap is a standby replaying a dropped index tripping an assertion in this codebase; the guarded code path is a deliberate no-op during recovery either way, so it has no effect on a release build, and a decision on whether to fix the assertion's contract or the call site is a separate, still-open question. A naive "fail on any trap" rule would make this job permanently red the moment that known case shows up in the test suite, so the classifier instead labels every trap or crash signal it finds as either that known case (including any TAP test exit that is a direct, traceable consequence of it) or something new, and only the second category fails the job.

A follow-up commit tightens classify_assert_results.sh's summary output: when a TAP file reported both a real test-logic failure and an unrelated crash signal in its own node logs, only the test-logic failure used to get itemized, because the loop returned early as soon as it found one. Node logs are now always scanned regardless, so a crash alongside a test-logic failure shows up in the printed summary instead of being silently dropped from it. This does not change the verdict or exit code in either case, since a test-logic failure alone was already enough to mark the run UNEXPECTED; it only affects how much detail the summary shows.

Related Issues

None.

Type of Change

  • Bug fix
  • New feature
  • Refactor / code cleanup
  • Documentation update
  • Test addition or update
  • Build / CI change

Pre-Merge Checklist

Build

  • make completes without errors or warnings
  • make install completes successfully

Tests

  • If this PR introduces no new behavior: existing regression tests (make installcheck) and TAP tests (test/t/) pass with no failures
  • If this PR introduces new behavior: test cases covering it were added to test/sql/ and/or test/t/
  • If this PR adds a standalone unit-test module under test/modules/: it builds and passes (make -C test/modules/<module> installcheck)

This PR touches no extension source and introduces no new product behavior, so the first box is the applicable one, but it is intentionally left unchecked rather than marked as a clean pass: see Testing Notes below for exactly what did and did not pass, and why.

Documentation

  • Relevant docs under docs/ updated if architecture or usage changed

No architecture or usage change; nothing under docs/ needed updating.

Testing Notes

Validated locally end to end against a from-scratch build with PG_CONFIGURE_EXTRA_ARGS="--enable-debug --enable-cassert --enable-injection-points --enable-tap-tests" and CFLAGS="-Og -g3 -fno-omit-frame-pointer", using the exact ci/build_and_test.sh steps the new workflow calls (not a GitHub Actions dispatch). pg_config --configure confirmed the flags landed in the built PostgreSQL. The canary step the workflow runs after the PostgreSQL install passed: a server with svs in shared_preload_libraries starts cleanly, SHOW debug_assertions reports on, and CREATE EXTENSION injection_points succeeds. make installcheck passed all 7 SQL regression tests with no assertion traps in the server log. make prove_installcheck ran all 51 TAP files and found the known trap (Assert("IsTransactionState()") in src/vamanacache.c) exactly as described, in two different TAP files whose scenario is a standby replaying a dropped index; ci/classify_assert_results.sh correctly labeled both occurrences KNOWN and did not fail on them. The same run also found one genuinely unexpected result, unrelated to the known trap and unrelated to anything this PR changes: a TAP file exercising error-handling return-value integrity around a checkpoint failure trips a plain SIGSEGV in the background worker, with no assertion trap at all, and this reproduced deterministically on an isolated rerun of just that one file. The classifier correctly flagged this UNEXPECTED and failed on it, which is the intended behavior for exactly this kind of finding; this failure is pre-existing in main's test suite under an assert build and is being reported separately rather than fixed as part of this PR, since this PR's own diff is CI-config and a classifier script only. actionlint reported no issues against the new workflow file, and shellcheck reported no issues against the new script.

One operational note for whoever does the first live trigger: the SVS install step clones ScalableVectorSearch at main and builds it against a pinned nightly release tarball of the C bindings. During this validation, main had drifted past that pin's API and failed to compile; this is a pre-existing risk in the already-merged build-and-test.yml job as well; not something introduced by this PR, since both jobs share the same pin, but worth knowing about if the SVS install cache is ever cold when this job runs.

The follow-up fix to classify_assert_results.sh was verified three ways: shellcheck is still clean against the changed script; the real captured logs from the original validation run above produce identical output to before, confirming no regression on the normal path; and a synthetic case (a fake TAP summary reporting one failed assertion alongside a fabricated SIGSEGV line in that same test's node log) now correctly itemizes both findings in the printed summary, where before only the test-logic failure would have appeared and the crash signal would have been silently omitted.

- Add a weekly, non-blocking workflow that builds PostgreSQL
  with --enable-cassert and runs the full regression and TAP suites
  against it, so invariant violations that Assert() catches (but a
  release build would silently tolerate or crash differently on) get
  caught somewhere
- Classify every assertion trap against one already-known, already-
  accepted case (a standby replaying a dropped index) so the job does
  not go permanently red over a condition everyone already knows about,
  while still failing loudly on anything else
- Never runs on pull_request or push, and is not a required check

Signed-off-by: Matt Welch <matt.welch@intel.com>
@matt-welch
matt-welch requested a review from a team October 6, 2026 22:00
@asonje

asonje commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Verified independently:

  • shellcheck ci/classify_assert_results.sh: clean, 0 issues.
  • actionlint .github/workflows/assert-build.yml: clean, 0 issues.
  • Traced the data flow against the existing ci/build_and_test.sh/Makefile this depends on: TAP_LOG_DIR=log,
    ci/.workdir/postgres.log, the PID-extraction regexes against real TRAP:/was terminated by signal message shapes, and the _*.log TAP node-log naming convention all line up with how this repo's suites actually log. No wiring bugs found.
  • Confirmed on: is schedule/workflow_dispatch only, never pull_request/push, so this correctly didn't run as a check on this
    PR itself.

One open question before merge, not a code bug: the testing notes mention a second, genuinely new finding from your own
validation run — a SIGSEGV in a TAP file around checkpoint-failure error handling, unrelated to the known vamanacache.c
trap. There's no tracking issue filed for it yet, and it's not in the classifier's known-list. Since the classifier
correctly labels unrecognized crashes UNEXPECTED, the first live run of this job (next scheduled trigger or a manual
dispatch) will go red for something that's already known at merge time — the exact failure mode the known/new split was
built to avoid. Could you file a tracking issue for it before merge? Separately, worth deciding whether to also add it to
the known-list (with a comment pointing at that issue) so the first run is actually green, or leave it red deliberately as a
forcing function — your call either way, just flagging that the choice isn't made yet.

Minor, non-blocking: in classify_log, if a single TAP file had both a real test-logic failure (is()/ok()) and an unrelated
crash in its node log, the continue right after flagging the test-logic failure skips checking that file's node logs, so the
crash wouldn't be individually itemized in the summary — unexpected_count is still correctly nonzero either way, so the
verdict/exit code is unaffected, this only affects how much detail shows up in the printed summary. Not asking for a fix,
just noting it in case it's worth a one-line comment for the next person reading this script.

- classify_assert_results.sh skipped checking a TAP file's node logs for
  TRAP/crash signals whenever that file also reported a real test-logic
  failure, so an unrelated crash in the same file would never show up in
  the printed summary
- Scan node logs regardless of whether the file also had a test-logic
  failure, and only suppress the generic "no trap found" fallback
  message when a cause has already been identified
- Does not change the verdict or exit code in any case: a test-logic
  failure already made the count nonzero either way

Signed-off-by: Matt Welch <matt.welch@intel.com>

@asonje asonje left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@matt-welch
matt-welch merged commit 87f13db into main Oct 8, 2026
5 checks passed
@matt-welch
matt-welch deleted the assert-build-ci-pass branch October 8, 2026 18:35
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