Skip to content

Per-shard coverage artifacts ship 8 copies of the source tree — 85.7% of the payload is text collect already has checked out #74

Description

@sotashimozono

Sharded coverage upload is 64× larger than it needs to be, and it exhausted the Actions artifact storage quota on lab-sotashimozono today — every PR in that org went red at Shard labels with

Failed to CreateArtifact: Artifact storage quota has been hit.

The shard jobs had already done their work; only the upload failed. No test ran on any PR after that point.

Where the bytes are

Julia writes *.cov as a 9-character counter column, one space, then a verbatim copy of the source line. Measured on one live testshards-coverage-s1 artifact from lab-sotashimozono/ParaLinearAlgebra.jl (48 files, 965,565 B uncompressed):

bytes share
counter columns 136,629 14.3 %
embedded source text 815,592 85.7 %

and only 2,134 of 15,181 lines (14.1 %) carry a counter at all — the rest are -.

Each of the 8 shards uploads the whole tree, not the files it happened to touch, so every run ships 8 near-identical copies of the package's source through artifact storage. Live totals in that one repo:

family objects live
coverage 625 170.84 MB
out 2410 28.46 MB
records 503 21.80 MB
lcov 298 4.63 MB
timings 81 0.12 MB

Per run: 8 coverage artifacts totalling 2.30 MB are merged into one lcov of 0.02 MB. The merged product is 1 % of its inputs.

The source is already at the destination

collect runs actions/checkout before download-artifact — deliberately, and the comment says why (Codecov needs the file network). So the 85.7 % that is source text is already present in that job's workspace, at the same paths, at the same revision. Verified: for all 48 files in that artifact, the text embedded in the .cov is byte-identical to the repo file at the run's head_sha.

Proposed change

Upload a sidecar of counted lines only — <lineno>,<count> — and have restore_counters rebuild the .cov files from the checkout.

Prototyped against the real artifact:

original .cov payload : 965,565 bytes
sidecar payload       :  15,049 bytes   -> 64.2x smaller
round-trip byte-identical: 48/48 files

Round trip is counter + checked-out source line -> "%9s %s", and it reproduces the original .cov exactly, so everything downstream of restore_countersCoverageTools, the lcov merge, the Codecov upload — is untouched.

This does not re-open the reason the conversion lives in collect

sharded-tests.yml records it:

and collect merges the results anyway, so doing it here does it N times for one answer. Measured on this repository it cost 3-25s per shard, the variance being a 20s JuliaSyntax precompile

That cost is Julia startup, not the conversion. The sidecar is a line filter — awk, no Julia, no depot, no precompile — so the objection does not apply to it. The merge still happens once, in collect, exactly as now.

Where it lands

  • sharded-tests.yml, "Collect this shard's raw coverage counters": write <path>.cov.idx instead of cping the .cov.
  • src/coverage.jl, restore_counters: accept .cov.idx and reconstruct against dest. Keeping it in the package rather than in the workflow is that file's own stated principle ("a merge written as a step script is unreachable from Pkg.test()").
  • Backward compatible: a parts/ tree containing real .cov files still restores by copy, so an in-flight run mid-upgrade is fine.

One new failure mode, and it should be loud

Reconstruction needs the source file present at dest with the same line count. If a counter file has no matching source, or the line counts disagree, that must refuse by name rather than silently emit a short .cov — a truncated counter file would under-report coverage with nothing to announce it. Worth carrying the line count in the sidecar header so the check is exact.

Happy to send the PR.

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