Skip to content

Do not fail coverage jobs when the profdata artifact is missing - #109250

Merged
maxknv merged 3 commits into
ClickHouse:masterfrom
groeneai:groeneai/coverage-optional-profdata-artifact
Jul 8, 2026
Merged

Do not fail coverage jobs when the profdata artifact is missing#109250
maxknv merged 3 commits into
ClickHouse:masterfrom
groeneai:groeneai/coverage-optional-profdata-artifact

Conversation

@groeneai

@groeneai groeneai commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Changelog category (leave one):

  • CI Fix or Improvement (changelog entry is not required)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

...

Description

Problem. Integration tests (amd_llvm_coverage, N/8) jobs (and the Functional / Unit test coverage siblings) intermittently finish with job-level check_status=error while every test row is OK and there is no timeout row. On the affected runs test_context_raw is just Failures: 0/625.

Root cause. The LLVM coverage jobs run their coverage merge non-blocking (force_ok_exit=True, "do not block pipeline"). llvm-profdata merge can crash (SIGSEGV) while reading a corrupt .profraw header:

Collecting and merging LLVM coverage files...
Merging 9 profraw files into ./it-amd_llvm_coverage_6_8.profdata
ERROR: Failed to create final coverage file
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ ...
 #7 llvm::readAndDecodeStrings(...)
 #8 llvm::InstrProfSymtab::create(...)
 #11 llvm::RawInstrProfReader<unsigned long>::readHeader()
...
=========== 621 passed, 4 skipped, 85 warnings in 2947.72s (0:49:07) ===========

merge_profraw_files() then returns None and writes no ./*.profdata. So far the job is still green. But each coverage job also declares provides=[LLVM_COVERAGE_FILE_*] with path=['./*.profdata'], and the praktika post-run upload treats a missing providing artifact as a hard error:

Job provides s3 artifacts [[Artifact.Config(name='LLVM_COVERAGE_FILE_it_6', ... path=['./*.profdata'] ...)]]
ls: cannot access './*.profdata': No such file or directory
ERROR: Failed to upload artifact [LLVM_COVERAGE_FILE_it_6:./*.profdata], ex [Artifact ./*.profdata not found]

result.set_status(Result.Status.ERROR) reddens the whole job even though 0 tests failed. The "do not block pipeline" intent only made the test result non-blocking; the mandatory provides-artifact upload runs afterward and hard-fails on the missing file.

Fix. Add an optional flag to Artifact.Config. When a providing artifact marked optional matches no file at upload time, the runner skips it with a warning instead of setting ERROR. The coverage profdata artifacts are marked optional. This is safe because the downstream LLVM Coverage aggregation job already globs whatever .profdata files exist (llvm-profdata merge -sparse -failure-mode=warn *.profdata, --ignore-errors), so a missing batch does not break aggregation. Non-optional artifacts (build binaries, etc.) keep the old strict behavior.

A regression test in ci/tests/test_optional_artifact.py asserts that the coverage profdata artifacts are optional, that optional defaults to False (backward compatible), and that the runner skips a missing optional artifact but still errors on a missing required one.

Version info

  • Merged into: 26.7.1.669 (included in 26.7 and later)

LLVM coverage jobs (Integration, Functional and Unit tests, amd_llvm_coverage)
run their coverage merge non-blocking: llvm-profdata can crash on a corrupt
.profraw and produce no .profdata, but the tests all pass and the job is meant
to stay green ("do not block pipeline").

Each such job also declares provides=[LLVM_COVERAGE_FILE_*] with path
./*.profdata. The praktika post-run upload treated a missing providing artifact
as a hard error and set the job to check_status=error even though 0 tests
failed. Observed on several PRs where job.log shows "Failed to create final
coverage file" (llvm-profdata crash) followed by "Failed to upload artifact
[LLVM_COVERAGE_FILE_it_N:./*.profdata]".

Add an optional flag to Artifact.Config. When a providing artifact marked
optional matches no file at upload time, skip it with a warning instead of
reddening the job. Mark the coverage profdata artifacts optional. The
downstream LLVM Coverage aggregation already globs whatever .profdata files
exist, so a missing batch is tolerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@groeneai

groeneai commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author
Pre-PR validation gate (click to expand)
# Question Answer
a Deterministic repro? Yes. The defect (a job with all tests OK reddened solely because a non-blocking-merge provides artifact is missing) is reproduced deterministically by ci/tests/test_optional_artifact.py, which drives the exact runner decision. The upstream trigger (llvm-profdata SIGSEGV on a corrupt .profraw) is non-deterministic, but the defect being fixed is the mandatory upload of a legitimately-absent artifact, which is fully deterministic.
b Root cause explained? Coverage jobs run the merge non-blocking (force_ok_exit=True). llvm-profdata merge crashes on a corrupt .profrawmerge_profraw_files() returns None, no ./*.profdata is written. The job is still green, but it declares provides=[LLVM_COVERAGE_FILE_*] path=['./*.profdata']; praktika's post-run upload (runner.py) runs because do_not_block_pipeline_on_failure() is True, ls -l ./*.profdata finds nothing, and the exception sets Result.Status.ERROR → job goes red despite 0 test failures.
c Fix matches root cause? Yes. The mismatch is that a non-blocking job's provides artifact upload is nonetheless mandatory. Fix adds Artifact.Config.optional; a missing optional artifact is skipped with a warning instead of setting ERROR. Not a band-aid: it aligns upload semantics with the already-non-blocking merge semantics.
d Test intent preserved / new tests added? New test ci/tests/test_optional_artifact.py added: asserts coverage profdata artifacts are optional, optional defaults to False (backward compat), the runner skips a missing optional artifact, and still errors on a missing required one (strictness preserved for build binaries etc.).
e Both directions demonstrated? Yes. Against unfixed sources the 5 tests FAIL (TypeError: ... unexpected keyword argument 'optional' and helper missing); with the fix all 5 PASS. Verified by git stash of the 3 source edits and re-running pytest.
f Fix is general across code paths? Yes. The same fragility exists in integration_test_job.py, functional_tests.py, and unit_tests_job.py (all provide coverage profdata and attach it only if os.path.exists). The fix is at the shared praktika artifact layer + a single defs.py artifact definition (.parametrize), so all 20 LLVM_COVERAGE_FILE_* artifacts (FT + IT + unit) are covered at once, not one job.
g Fix generalizes across inputs? N/A (CI harness robustness fix, not an engine/data-type code bug). The relevant "inputs" — every coverage batch/lane that provides a profdata artifact — are all covered via the parametrized LLVM_ARTIFACTS_LIST.
h Backward compatible? Yes. optional defaults to False, so every existing artifact keeps strict behavior; only the coverage profdata artifacts opt in. No setting/format/metadata change.
i Invariants and contracts preserved? Yes. Non-optional artifacts still hard-fail when missing (build-artifact contract unchanged). Downstream LLVM Coverage aggregation already globs *.profdata with -failure-mode=warn / --ignore-errors and was success on PR #109097 despite the missing batch, so tolerating a missing batch does not violate the aggregation contract. glob.glob() (used to feed copy_file_to_s3) is now the single existence gate, removing a TOCTOU gap vs the prior ls -l check.

Additional context: the job-level-error-with-all-tests-OK signature was cited on 4 PRs on 2026-07-02 (batches 5/8 + 6/8), 0 on master. Example failing report: PR #109097, Integration tests (amd_llvm_coverage, 6/8) - its job.log shows the llvm-profdata crash followed by the artifact-upload error.

Session id: cron:clickhouse-worker-slot-1:20260703-021200

@groeneai

groeneai commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

cc @maxknv @leshikus - could you review this praktika change? When an LLVM coverage job's llvm-profdata merge crashes on a corrupt .profraw, no ./*.profdata is written; the job's tests all pass and it is non-blocking, but the mandatory provides artifact upload then fails and reddens the job to check_status=error. This adds an optional flag to Artifact.Config so a missing optional artifact is skipped with a warning instead of erroring, and marks the coverage profdata artifacts optional (the downstream LLVM Coverage aggregation already tolerates a missing batch).

@PedroTadim PedroTadim added the can be tested Allows running workflows for external contributors label Jul 3, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [cf8cb51]

Summary:


AI Review

Summary

This PR adds Artifact.Config.optional and marks LLVM_COVERAGE_FILE_* uploads optional so coverage jobs stay green when llvm-profdata produces no .profdata. I do not think the current optional on every run behavior is safe to merge: it reopens the master-baseline problem that the earlier 814773d revision fixed, so master can now publish a partial llvm_coverage.info and later PRs will diff against a corrupted baseline.

Findings

❌ Blockers

  • [ci/praktika/runner.py:595-605, ci/defs/defs.py:581-587] [dismissed by author -- https://github.com/Do not fail coverage jobs when the profdata artifact is missing #109250#issuecomment-4914812333] Making LLVM_COVERAGE_FILE_* optional on master violates the existing coverage-baseline contract documented in [ci/jobs/scripts/workflow_hooks/filter_job.py:351-354]. When a master coverage shard's non-blocking llvm-profdata merge emits no .profdata, _post_run now skips the missing artifact and keeps the shard green; the final coverage job still merges whatever *.profdata are present ([ci/jobs/scripts/merge_llvm_coverage.sh:52-63]) and publishes that partial llvm_coverage.info, which later PRs download from REFs/master/... as their baseline ([ci/jobs/scripts/generate_diff_coverage_report.sh:21-31]). The effect is artificial coverage deltas and "newly covered" spikes on unrelated PRs, plus master stops failing noisy when a coverage shard silently dropped out.
    Suggested fix: restore the PR-only guard from 814773d (or add an equivalent final-job completeness check) so missing coverage shards stay non-blocking on PRs but remain fail-noisy on master/release.
Tests
  • ⚠️ [ci/tests/test_optional_artifact.py:27-28, 71-90] The current regression test now asserts the broken master behavior ("skip on any run"), so it would preserve the partial-baseline bug instead of catching it. Please restore focused coverage for the master path as part of the fix.
Final Verdict
  • Status: ⚠️ Request changes
  • Minimum required actions: keep missing LLVM_COVERAGE_FILE_* artifacts optional only on PR runs (or block publication of incomplete master coverage baselines), and restore a regression test that proves the master path stays fail-noisy.

@clickhouse-gh clickhouse-gh Bot added the pr-ci label Jul 3, 2026
Comment thread ci/defs/defs.py
# tolerated by the downstream LLVM Coverage aggregation, which globs
# whatever .profdata files exist, so a missing file must not redden a
# coverage job whose tests all passed.
optional=True,

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.

Marking every LLVM_COVERAGE_FILE_* artifact optional here applies to the master workflow too, not just PR coverage jobs. That breaks an invariant we already rely on in ci/jobs/scripts/workflow_hooks/filter_job.py: lines 342-345 explicitly say master must always publish a complete llvm_coverage.info, otherwise later PRs get artificial coverage deltas from a partial baseline. With this change a shard that fails its local llvm-profdata merge will stay green, Runner._post_run will silently skip the missing artifact, and the final merge still proceeds over whatever *.profdata happen to exist (ci/jobs/scripts/merge_llvm_coverage.sh, lines 52-72). The result is that master can now upload an incomplete baseline instead of failing noisy.

Can we keep the non-blocking behavior for PR jobs without weakening master? Two options that would preserve the coverage contract are:

  1. make the artifact optional only on PR workflows, or
  2. keep the producer green but have the final LLVM Coverage job detect missing expected shards and refuse to publish llvm_coverage.info on master.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Confirmed and fixed in 814773d. You are right: the blanket optional=True in defs.py is shared by the master workflow too, so a master shard whose non-blocking llvm-profdata merge crashed would have stayed green and published a partial baseline, giving later PRs artificial deltas.

Went with option 1 (PR-only). The artifact config in defs.py is static and shared by both workflows, so it cannot distinguish PR from master; instead the gate is at the runtime skip decision, where the event is known. Runner._skip_missing_optional_artifact now takes env.PR_NUMBER and returns True only when pr_number > 0. On master/release runs (pr_number <= 0: push, schedule, dispatch, merge-queue) a missing artifact is still an error even when optional, so that shard job reddens, the run fails noisy, and the final merge never publishes an incomplete llvm_coverage.info. Non-optional artifacts error on any run, as before.

Regression tests lock this in: test_runner_does_not_skip_missing_optional_artifact_on_master (asserts pr_number 0 and -1 both error) alongside the PR-run skip case and the non-optional error case.

The optional flag on the LLVM coverage profdata artifacts made a missing
.profdata non-blocking on every workflow, including master. But master must
publish a complete llvm_coverage.info baseline (see
ci/jobs/scripts/workflow_hooks/filter_job.py): if a master shard's non-blocking
llvm-profdata merge crashes and produces no file, staying green would upload a
partial baseline and later PRs would diff against it and report artificial
coverage deltas.

Gate the skip to PR runs (env.PR_NUMBER > 0). On master/release runs a missing
artifact is still an error even when optional, so master stays fail-noisy and
the baseline stays complete. PR coverage jobs keep the non-blocking behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@clickhouse-gh clickhouse-gh Bot added the manual approve Manual approve required to run CI label Jul 3, 2026
@groeneai

groeneai commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Updated the fix per the review (commit 814773d): the optional-artifact skip is now gated to PR runs so master keeps a complete coverage baseline.

Pre-PR validation gate (click to expand)
# Question Answer
a Deterministic repro? Yes. Runner._skip_missing_optional_artifact(optional_artifact, path, pr_number) returns True for pr_number>0 and False for pr_number<=0; asserted directly in the unit tests (0.16s, no server).
b Root cause explained? The optional=True flag lives in static defs.py config shared by the PR and Master workflows. The runtime skip in _post_run ignored the event, so on master a shard whose non-blocking llvm-profdata merge crashed stayed green, merge_llvm_coverage.sh merged over whatever .profdata existed, and master published a partial llvm_coverage.info baseline. filter_job.py:342-345 documents that master must publish a COMPLETE baseline or later PRs see artificial coverage deltas.
c Fix matches root cause? Yes. The skip decision is the exact point where PR vs master is known (env.PR_NUMBER), so gating the skip there (optional and pr_number>0) preserves the master baseline invariant without weakening PR non-blocking behavior. No band-aid.
d Test intent preserved / new tests added? Yes. Added test_runner_does_not_skip_missing_optional_artifact_on_master (pr_number 0 and -1 both error) and kept the PR-run skip + non-optional error cases. The prior single-arg test was updated to pass pr_number.
e Both directions demonstrated? Yes. PR run (pr_number=12345) + optional missing -> skip (green); master run (0/-1) + optional missing -> error (fail-noisy); non-optional missing -> error on any run. 6/6 tests pass under pytest.
f Fix is general across code paths? Yes. _skip_missing_optional_artifact is the sole skip decision (one caller in _post_run); no sibling skip path exists. The producer stays green on PR via do_not_block_pipeline_on_failure(), unchanged.
g Fix generalizes across inputs? Yes. Covers all non-PR events that set PR_NUMBER=0 (push, schedule, dispatch, merge-queue) and the workflow-config probe (-1); optional and non-optional artifacts both handled.
h Backward compatible? Yes. optional still defaults to False; non-optional behavior is unchanged. No setting/format/on-disk change. This change RESTORES the pre-existing master baseline contract that the first revision had inadvertently weakened.
i Invariants and contracts preserved? Yes. This is the invariant the bot flagged: master publishes a complete coverage baseline (filter_job.py:342-345). The fix makes master fail-noisy on any missing shard artifact, so an incomplete baseline is never published. PR-side non-blocking coverage behavior is preserved.

Session id: cron:clickhouse-worker-slot-8:20260703-074200

@groeneai

groeneai commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

CI finish ledger — 814773d

CI fully finished on head 814773d74cea (Finish Workflow + Mergeable Check green; 154 checks pass, 0 fail/pending). No test failures across the run (CIDB: 230 rows, 0 FAIL/ERROR). No failure to own; nothing PR-caused.

Check / test Reason Owner / fixing PR
(none) CI clean n/a

Session id: cron:our-pr-ci-monitor:20260707-040000

@leshikus
leshikus self-requested a review July 7, 2026 17:59
@leshikus

leshikus commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Suggest naming the flag optional_on_pr rather than optional. As written, optional reads as a generic "may be absent" flag, but the runner hardcodes it to "optional on PRs, required on master".

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

Other than the name, looks good

@maxknv
maxknv self-requested a review July 8, 2026 11:37
@maxknv

maxknv commented Jul 8, 2026

Copy link
Copy Markdown
Member

Suggest naming the flag optional_on_pr rather than optional. As written, optional reads as a generic "may be absent" flag, but the runner hardcodes it to "optional on PRs, required on master".

@groeneai , better to make it optional for all cases

@maxknv
maxknv marked this pull request as draft July 8, 2026 12:26
auto-merge was automatically disabled July 8, 2026 12:26

Pull request was converted to draft

Per reviewer decision: a missing optional providing artifact is skipped
with a warning on any run (PR, master or release), not gated to PR runs.
The flag stays named optional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@groeneai

groeneai commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

Done in cf8cb51. The runner now skips a missing optional artifact with a warning on any run (PR, master and release); a missing non-optional artifact still errors. Flag stays named optional. This supersedes the earlier PR-only gating (the clickhouse-gh[bot] master-baseline CR), per your decision to make it optional everywhere. Tests in ci/tests/test_optional_artifact.py updated to match.

@groeneai

groeneai commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author
Pre-PR validation gate (click to expand)
# Question Answer
a Deterministic repro? Yes. test_runner_skips_missing_optional_artifact and test_runner_does_not_skip_missing_required_artifact deterministically exercise the runner decision; the original job-reddening was reproduced from job.log (llvm-profdata SIGSEGV then Failed to upload artifact [LLVM_COVERAGE_FILE_*]) on PRs #109097/#108909/#104217/#97032.
b Root cause explained? The provides artifact upload treated a missing file as a hard error even when the coverage merge legitimately produced none (non-blocking, can crash on corrupt .profraw).
c Fix matches root cause? Yes. An optional artifact that matches no file is skipped with a warning instead of erroring.
d Test intent preserved / new tests added? Yes. Tests assert optional-missing is skipped on any run and required-missing still errors; optional defaults False (backward compatible).
e Demonstrated both directions? Yes. optional-missing -> skip (green); required-missing -> error.
f Fix general, not a narrow patch? Yes. optional is a generic Artifact.Config flag honored uniformly in the single upload path.
g Generalizes across inputs? N/A (CI-config/runner behavior change, not a datatype/engine code path).
h Backward compatible? Yes. optional defaults False; all existing artifacts unchanged; no format/setting change.
i Invariants and contracts preserved? Per reviewer decision (@maxknv), coverage artifacts are optional everywhere; the LLVM Coverage aggregation already globs whatever .profdata exist. This supersedes the earlier "keep master baseline complete" gating.

Session id: cron:clickhouse-worker-slot-6:20260708-123000

@maxknv
maxknv marked this pull request as ready for review July 8, 2026 13:19
@maxknv
maxknv enabled auto-merge July 8, 2026 13:19
@maxknv
maxknv added this pull request to the merge queue Jul 8, 2026
@groeneai

groeneai commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

CI finish ledger — cf8cb51

CI fully finished, all green. No failed checks.

Check / test Reason Owner / fixing PR
(none)
Sync CH Inc sync (private, not actionable)

Session id: cron:our-pr-ci-monitor:20260708-143000

Merged via the queue into ClickHouse:master with commit 94ac2ef Jul 8, 2026
175 checks passed
@robot-ch-test-poll2 robot-ch-test-poll2 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jul 8, 2026
zeekay pushed a commit to hanzoai/datastore that referenced this pull request Sep 11, 2026
…optional-profdata-artifact

Do not fail coverage jobs when the profdata artifact is missing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

can be tested Allows running workflows for external contributors manual approve Manual approve required to run CI pr-ci pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants