Skip to content

build_once() trusts a source fingerprint, so the pytest corpus silently runs against a library another harness installed #956

Description

@OffgridwithJD

build_once() in test/pytest/pgc_cluster.py keys its skip marker on the source fingerprint and never looks at the installed library. So anything else that writes the shared prefix — the shell harness, a perf run, a manual make install, another worktree — leaves the pytest corpus running against a library it did not build, reporting already-built while doing it.

This is not hypothetical. It cost two sessions today and the evidence was printed in both.

Measured: two independent incidents

Mine, reviewing #955. The corpus showed 10 failures, all in test_join_runtime_filter.py, on the PR's tree and on plain main. Neither was red. My instruction-counter run had installed a pre-#945 library into /usr/local/pg18a. Rebuilding from main took that file from 10 failed to 11 passed.

@jdatcmd's, on their own box, after I flagged it. /usr/local/pg17 was holding a pre-#945 library and they ran #945's suite through the skip-build path:

stale .so 9edef563e34d   44 checks, 25 passed + 19 FAILED
restored  2c9559d087b0   44 checks, 44 passed +  0 failed

Nineteen failures in one file with the code entirely innocent.

Reproduced deliberately

baseline
  marker: /usr/local/pg18a/bin/pg_config|18|a0e6afc3e13e
  .so:    f0b770d93a2a      (built from /root/wmp = main)
  result: 11 passed

install a pre-#945 .so, VALID for this prefix, marker untouched
  .so:    d312a10c0cfb
  marker: /usr/local/pg18a/bin/pg_config|18|a0e6afc3e13e     <- unchanged

run the same tests from main again
  -- build: already-built from /root/wmp
  -- cluster: worker=master port=29768 PostgreSQL 18.4 .so=d312a10c0cfb
  FAILED test_serial_join_runtime_filter
  FAILED test_scattered_join_runtime_bloom
  FAILED test_cross_type_int4_int8_bloom
  FAILED test_collation_mismatch_bloom_only

restore main's .so
  result: 11 passed

The mechanism, and why the existing guard misses it

pgc_cluster.py:438 keys the marker on pg_config, major and source_fingerprint(srcdir). Its own comment says the fingerprint is in the key because keying on pg_config and major alone "would skip the build after a source edit, which is the staleness this whole guard exists to stop."

That is right as far as it goes. The guard detects staleness caused by editing the source. It cannot detect staleness caused by someone else overwriting the prefix, because the key describes the source tree and never observes the artifact. marker matches means "the pytest layer last built this source", which is a different claim from "the prefix holds that build".

The two lines that disagree, in one output

-- build: already-built from /root/wmp
-- cluster: ... .so=d312a10c0cfb

The first names a tree and reads as provenance. The second carries the actual identity. d312a10c0cfb is not a build of /root/wmp. Both of us walked past it, and conftest.py:84 says the fingerprint is printed "so a reader can tell which binary produced the results below" — it does that job, and it is sitting next to a line that contradicts it.

Failure distribution is the diagnostic

Worth recording because it is what should have tipped us off sooner: a stale library's blast radius is exactly the feature that library lacks, so it presents as one whole file failing while the rest of the corpus passes. Scattered failures across files are usually the code; a clean file boundary is usually the environment.

Proposed

Record the installed library's fingerprint in the marker alongside the source fingerprint, and compare the file on disk against it before returning already-built. A mismatch means a third party wrote the prefix, so rebuild.

One trap for whoever implements it: the .so md5 is not a function of the source alone — the build path is compiled in. Main built from /root/wmainperf gave 5b294366cc25 and from /root/wmp gave f0b770d93a2a. So the fix cannot compare against a stored per-source constant. It must store the md5 that was installed when the marker was written and detect any later change to the file.

so_md5() already exists in the cluster helper, so the observation is available and simply is not part of the decision.

Out of scope

Stopping other harnesses from writing the prefix. They legitimately install there; the fix is for the pytest layer to notice rather than for anyone to stop.

Not a defect, for contrast, and worth keeping

Cross-major contamination already fails closed. Installing a PG 17 build into a PG 18 prefix gives FATAL: could not load library ... undefined symbol: smgrtruncate2 and the cluster refuses to start. It is cross-tree contamination within one major that fails open. My first reproduction attempt hit the loud path by accident — my own worktree held PG 17 objects — and I mention it because it is the reason the quiet path took a second attempt to show.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions