Pre-merge coverage resultsets in the RSpec artifact collectors
What does this MR do and why?
rspec:coverage and rspec:undercoverage run one after the other after the last RSpec job, and end most tier 3 pipelines (#636505 (closed)). rspec:coverage collates about 300 SimpleCov resultsets, one per RSpec job. In job 17491331510, downloading them took 51s and merge-simplecov 2m14s.
This MR has each rspec:artifact-collector job merge its group's resultsets into one before it uploads them, so rspec:coverage downloads and collates about 7 files instead. This was the open follow-up in #629055 (closed).
scripts/premerge-simplecovmerges the resultsets with SimpleCov's own merger, writes one resultset and only then deletes the inputs.- The merged coverage is written unfiltered. Filtering needs the source files, which the collectors don't have, so
rspec:coveragestill applies every filter, as it does today. - It leaves the resultsets as they are when there is only one.
- The collectors extend
.fast-no-clone-joband use the Ruby alpine image, asretrieve-tests-metadatadoes: they download onlyscripts/premerge-simplecovandGemfile.lockthrough the Files API, then install thesimplecovgem at the locked version. If the merge itself fails,scripts/premerge-simplecovleaves the resultsets as they are.
Results
The collectors are on the critical path too: the last one to finish, usually rspec:artifact-collector ee unit, starts right after the last RSpec job. So this only helps if what it adds there costs less than what it saves in rspec:coverage.
Tier 3 pipeline of this MR (pipeline 2996913085) against another MR's tier 3 pipeline (pipeline 2992718000), by job section:
rspec:artifact-collector ee unit:
| Section | Before (job 17491331508) | With this MR (job 17523326031) |
|---|---|---|
| Runner setup | 5s | 7s |
| Download artifacts | 33s | 35s |
Script: download files, install simplecov, pre-merge |
1s | 32s (merge: 29.0s) |
| Upload artifacts | 6s | 2s |
| Total | 46s | 78s |
rspec:coverage:
| Section | Before (job 17491331510) | With this MR (job 17523326033) |
|---|---|---|
| Runner setup | 22s | 19s |
| Restore cache | 20s | 17s |
| Download artifacts | 51s | 29s |
Script (merge-simplecov and splitting coverage.xml) |
160s | 79s |
| Upload artifacts | 7s | 5s |
| Total | 267s | 154s |
Time from the last RSpec job finishing to the end of each job, against the medians of 113 tier 3 MR pipelines from 2026-10-26 and 2026-10-27:
| Median before (p20–p80) | With this MR | |
|---|---|---|
| Last collector | 46s (33–51s) | 80s |
rspec:coverage |
274s (192–317s) | 236s |
rspec:undercoverage |
371s (294–445s) | 323s |
So the slowest collector takes about 30s longer, rspec:coverage about 75s less than its median of 228s (p20–p80: 190–260s), and the pipeline ends roughly 45s sooner. One pipeline is within the normal range of the medians, so this needs confirming over more pipelines after merge.
Other runs of this MR:
- Pipeline 2995460253 used the full repository from
clone-gitlab-repoand the Ruby slim image in the collectors. That added about 20s of downloads and 15s of runner setup torspec:artifact-collector ee unit(112s), so the pipeline wasn't faster (399s to the end ofrspec:undercoverage). - In pipeline 2996662918, single artifact downloads of RSpec jobs stalled for 135–269s in
rspec:artifact-collector ee unitandee remainder, which happens with or without this MR. Apart from that, the collectors' script took the same time as above (31s inee unit, merge 29.8s), andrspec:coveragetook 145s.
The merged coverage is the same as before: every resultset of the RSpec jobs that ran reaches rspec:coverage (rspec-ee fail-fast uploads none), and building the reports from the pre-merged resultsets of pipeline 2992718000 gives byte-identical lcov and coverage.xml files (apart from the timestamp).
How to set up and validate locally
- Download the artifacts of the RSpec artifact collectors of a tier 3 pipeline, and extract each one into its own directory.
- Check out the pipeline's commit, and replace
/builds/gitlab-org/gitlab/in the.resultset.jsonfiles with the path of that checkout. Leave/builds/gitlab-org/gitlab-foss/paths as they are, as they are in CI. - In each collector's directory, run
scripts/premerge-simplecov premerged-<collector>. - Copy the resulting
coverage/*directories into onecoverage/directory, and runscripts/merge-simplecovagainst it, with the original resultsets in another directory. - Compare
coverage/lcov/*.lcovandcoverage/coverage.xml(without thetimestampattribute) between the two.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.