Commit 2026-08-28 00:22 5ba95124
View on Github →fix(cache): retry transport errors, improve transfer robustness (#43145)
Since CI cache reads moved to cache.mathlib.org (#42777), the transport is HTTP/2: one connection carries many parallel transfers, and a dropped connection fails them all at once with Send failure: Broken pipe. curl's --retry does not cover transport errors, so a short network fault fails the job. Seven bors runs failed this way in six days (example: 14 of 8750 files, one connection); the same step failed zero times in the previous four weeks (429 runs).
Fix: --retry 5 --retry-all-errors on the parallel download path and the upload path (curlRetryArgs). curl retries a failed transfer on a fresh connection, with exponential backoff. An HTTP 4xx is not a curl error on these paths, so a 404 miss never retries and the container fallback works as before.
- The serial download path keeps plain
--retry 5: it serves curls below 7.71, which reject--retry-all-errors. It now drops--failand decides by the written-out status, like the parallel path, so no download path treats a miss as an error. - The marker probe keeps no retry flags: it is diagnostic, a false negative is cheap, and retry backoff would stall the up-to-50-probe
cache querywalk against an unreachable endpoint. It adds--connect-timeout 10 --max-time 30as time bounds. - A retried PUT is safe: a non-overwrite put sends
If-None-Match: *, and a duplicate answers 409/412, which the upload path already excuses. validateCurlraises the parallel-mode floor to curl 7.75:--retry-all-errorsneeds 7.71, and the per-transfer JSON report carriesexitcodeanderrormsgfrom 7.75 on. CI runners have 8.5; old Linux curls already fetch a static 7.88.1. An outage that outlives the five retries still fails the step. This PR also fixes five pre-existing bugs in these paths:- A transfer can die after the 200 status arrives: curl reports
http_code: 200with a nonzero exit code, but the code matched the status alone and renamed the truncated part file into the cache, where only a manual clean removed it. A transfer now counts as delivered only when curl also exits cleanly. The verdict is a pure table with unit tests (classifyDownload/classifyUpload). - An error answer to a PUT carries a body, which landed on stdout and glued to curl's per-transfer JSON report. The report did not parse, so a 409 did not count as a skip and a 403/404/500 did not count as a failure — the upload step passed either way. The put config now discards each response body, so stdout carries only the reports.
- On a miss, the parallel path left the error body behind as a
.partfile. It now removes the file, like the serial path. - A 404 answer to a PUT was excused as a cache miss. It now counts as an upload failure.
- The marker PUTs carry their credential in the curl argument list, and a failure message echoed that list. Their failure messages now name the command, its exit state, and stderr only. (In CI the token is also masked in logs; this guards local runs and transformed output.)
Tested with
lake build cache, thecache-testsuite, and a forced re-download of 64 files throughcache.mathlib.org. Against a local server that truncates every body, all 64 transfers fail and no truncated or partial file stays in the cache; against a server that answers 404, misses stay quiet and leave no partial files. For uploads, a put against a local server that answers 409 with an error body skips and exits 0; against a 404 server it reports the failed blob by URL and exits 1. Twoci_devruns built the branch's cache tool in CI: the download path served throughcache.mathlib.org, and the upload job wrote 3 files to the forks container over OIDC.