ci: bound the ffmpeg apt fetch so a stalled mirror costs a retry, not the job - #3356
Conversation
… the job Hosted runners intermittently stall on an apt mirror, and an unbounded apt-get inherits the whole job budget. The producer integration lane normally finishes in ~11 minutes against a 20 minute cap; on a stalled fetch it ran to the cap and failed. Same step, same shape, reproduces on main's tip — it is not specific to any one PR. The cost is not one red check. On the run that prompted this, four went red off that single step: the two jobs that install ffmpeg, plus a Test gate and a preview-regression gate that both fail closed when their dependency does not succeed. So a mirror stall reads as a producer defect and a preview defect. Each attempt is now bounded and retried three times, and the five workflows that installed ffmpeg share one action instead of five copies of the command. Deliberately still apt: caching the binary would strip it from the shared libraries it links against, and switching to a static build would change the ffmpeg under the producer's output comparisons. Neither belongs in a fix for a network stall.
Converting the step to the shared action left the trailing `ffmpeg -version` line behind, and YAML folded it into the `uses:` value — so the runner looked for an action at a path with the command appended and failed all four perf shards. It parsed cleanly, which is why validating with a YAML load did not catch it: `uses: ./path\n ffmpeg -version` is a legal folded scalar. The check that does catch it asserts every local `uses:` resolves to a directory containing an action file, which is now what I ran. The action prints the version itself.
The first version wrapped apt in `timeout` and retried. A passing run showed why that is the wrong shape: the mirror is slow rather than hung — the install spent ~15 minutes pulling packages from azure.archive.ubuntu.com and finished successfully. Killing it at 300s discarded a download that was making progress and started over, so the retry turned a slow mirror into a slower one, and the worst case of three attempts exceeded the job's own 20 minute cap. Bound the connection instead. Acquire::Retries re-fetches the one package whose connection stalled while keeping everything already downloaded, and Acquire::http::Timeout caps how long any single connection may sit idle. That addresses the stall the original report described without punishing the slow case that is far more common.
|
Correcting my own fix before it lands — a passing run showed the premise was wrong.
So the mirror is slow, not hung. My
The bound belongs on the connection rather than the command:
Worth recording that the evidence for this came from a green run. The check passed, so nothing would have flagged it — the 948-second step duration is the only thing that gave it away, and I only looked because 17m52s against a 20m cap seemed too close for a job that normally takes 11. Also carried from the earlier push: converting |
|
Green at 60 checks, and the timings show the corrected shape was the right call:
The middle row is the interesting one: it was green, which is why nothing flagged it. The wall-clock kill was discarding a download that was making progress and restarting it, so three attempts were needed to get through — 948 seconds for an install that takes about forty when the mirror behaves. Worst case would also have been 21 minutes against a 20 minute job cap, so the fix could still have burned the job it existed to protect. Bounding the connection instead lets apt re-fetch only the package that stalled and keep everything already downloaded. Same protection against the hang @jrusso1020 reported, without punishing the far more common slow case. Merging on Miguel's go-ahead. Happy to revisit if you'd rather it were shaped differently — you raised the original report, so the shape should be one you'd defend. |
Flagged by @jrusso1020 — the ffmpeg apt install stalls on hosted runners, and it reproduces on
main's tip, so it isn't specific to any one PR.What it costs
An unbounded
apt-getinherits the whole job budget. The producer integration lane normally finishes in ~11 minutes against a 20 minute cap; on a stalled fetch it ran to the cap and failed.The bill isn't one red check. On the run that prompted this, four checks went red off that single step:
Producer: integration testsInstall FFmpeg, 20m18sPreview parityInstall ffmpeg, 20m18sTestpreview-regressionPreview paritySo a mirror stall reads as a producer defect and a preview defect. Same fail-closed chain @jrusso1020 traced on #3306, different trigger.
Evidence it's the mirror and not the tests: the identical job passed in 10m40s on #3306 at the same time it hit the cap on #3305.
The fix
Each attempt is bounded (
timeout 120for update,timeout 300for install) and retried three times, so a stalled mirror costs seconds instead of the job. The five workflows that installed ffmpeg now share one action instead of five copies of the command.What this deliberately does not do
Both alternatives in the original suggestion were considered and rejected, and I'd rather say why than leave them looking unexplored:
ffmpeg-static, already a dependency) — this would change the ffmpeg binary underneath the producer's output comparisons.prepare-ffmpeg-binalready goes out of its way to avoid that CDN download, so the repo has evidently decided system ffmpeg is the source. A network-stall fix isn't the place to reopen it.If the stalls continue past this, the next honest step is caching the
.debset (which keeps the dynamic links intact), not the binary.Verification
YAML parses on all four changed workflows and the new action; the install script passes
bash -n. Beyond that this is only exercised by CI running it — the checks on this PR are the test.