[ci] Retry .NET SDK provisioning with exponential backoff#12120
[ci] Retry .NET SDK provisioning with exponential backoff#12120simonrozsival wants to merge 2 commits into
Conversation
The "install .NET SDK to bin/<Configuration>/dotnet" pipeline step intermittently fails with transient CDN network errors such as "curl: (56) Recv failure: Connection reset by peer". Add retry-with-exponential-backoff (5 attempts, 2s/4s/8s/16s) around both network operations in the provisioning scripts: - eng/install-dotnet.sh: new retry() helper wraps the dotnet-install.sh download and the SDK install invocation; curl also gains --retry 5 --retry-delay 1 --retry-all-errors and an EXIT trap to clean up the temp file. - eng/install-dotnet.ps1: new Invoke-WithRetry helper wraps Invoke-WebRequest and the install-script invocation (now throwing on non-zero exit so retries trigger). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 85e98740-084d-4e54-9512-ea3cf95a1436
5745544 to
d47d26c
Compare
There was a problem hiding this comment.
Pull request overview
This PR aims to harden CI/local provisioning of the repo-local .NET SDK (bin/<Configuration>/dotnet) against transient network/CDN failures by adding retry logic with exponential backoff around both the dotnet-install script download and the SDK install step.
Changes:
- Add an exponential-backoff retry helper to the Bash provisioning script and wrap both network operations.
- Add an exponential-backoff retry helper to the PowerShell provisioning script and ensure non-zero exit codes trigger retries.
Show a summary per file
| File | Description |
|---|---|
| eng/install-dotnet.sh | Adds retry() helper and wraps curl download + dotnet-install.sh invocation with exponential backoff. |
| eng/install-dotnet.ps1 | Adds Invoke-WithRetry helper and wraps Invoke-WebRequest + dotnet-install.ps1 invocation with exponential backoff + explicit non-zero exit handling. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 3
- eng/install-dotnet.sh: drop curl's own --retry/--retry-all-errors flags (--retry-all-errors isn't supported by the older system curl on some macOS images); the outer retry() helper already backs off on any failure, including transport errors like "curl: (56)". - eng/install-dotnet.ps1: only pass -UseBasicParsing on Windows PowerShell (Desktop); on PowerShell 7+ (Core) it's unnecessary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 85e98740-084d-4e54-9512-ea3cf95a1436
|
Addressed the review findings in a follow-up commit:
|
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
🤖 Code Review — ✅ LGTM (with minor suggestions)
Wraps both network operations in eng/install-dotnet.sh and eng/install-dotnet.ps1 with a 5-attempt exponential-backoff retry to absorb transient .NET CDN failures. The implementation is correct and well-commented.
Verified:
- Bash —
retry()composes safely withset -euo pipefail(theif "$@"guard suppresses errexit per-attempt), thecurl -o tmp+ atomicmvcache pattern is preserved, and theEXITtrap is correctly scoped and cleared after success. Removing curl's own--retry-all-errors(unsupported on older macOS curl) in favor of the outerretry()is reasonable. - PowerShell —
Invoke-WithRetryrethrows after$MaxAttemptsand the install-script wrapper converts a non-zero$LASTEXITCODEinto a throw so retries actually trigger. Edition-gating-UseBasicParsing(Desktop only) is correct. Backoff schedule (2s→4s→8s→16s) matches the comments. - CI — All 44
dotnet-androidchecks are green.
Suggestions (non-blocking): 2 💡 inline comments — retry scope for non-transient failures, and PowerShell try-block indentation.
Nice, focused reliability fix. 👍
Issues: ❌ 0 ·
Generated by Android PR Reviewer for #12120 · 77.3 AIC · ⌖ 13 AIC · ⊞ 6.8K
Comment /review to run again
|
|
||
| echo "Installing .NET SDK $sdk_version into $install_dir" | ||
| bash "$install_script" --version "$sdk_version" --install-dir "$install_dir" --no-path | ||
| retry bash "$install_script" --version "$sdk_version" --install-dir "$install_dir" --no-path |
There was a problem hiding this comment.
🤖 💡 Patterns — retry() retries on any non-zero exit, so a genuinely deterministic failure (bad SDK version → curl -f HTTP 404/exit 22, or dotnet-install.sh rejecting an invalid --version) now burns all 5 attempts and ~30s of backoff before surfacing. That's an acceptable tradeoff for a provisioning script, but consider narrowing retries to transient transport errors (or at least noting in the header comment that non-transient failures are also retried) so future readers aren't surprised by the delay on a hard failure.
(Rule: Retry scope — non-transient failures)
| # -UseBasicParsing is required on Windows PowerShell (Desktop) to avoid | ||
| # the Internet Explorer engine; on PowerShell 7+ (Core) it is the only | ||
| # mode and the parameter is unnecessary, so gate it by edition. | ||
| $iwrArgs = @{ |
There was a problem hiding this comment.
🤖 💡 Formatting — The body inside this try { ... } block (lines 67–79) is indented one level shallower than the surrounding try/finally (compare Move-Item on the next block, which is at 4 spaces). Re-indenting the $iwrArgs hashtable and Invoke-WithRetry block to match keeps the try body visually nested. Cosmetic only.
(Rule: Consistent indentation)
Problem
The install .NET SDK to bin//dotnet pipeline step intermittently fails with transient CDN network errors, e.g.:
Example: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1508469
There was no retry/backoff around the download or install, so a single dropped connection to the .NET CDN failed the whole job.
Fix
Add retry-with-exponential-backoff (5 attempts, 2s → 4s → 8s → 16s) around both network operations in the provisioning scripts — the
dotnet-installscript download and the SDK install itself (which also downloads from the CDN):eng/install-dotnet.sh: newretry()helper wraps thedotnet-install.shdownload and the SDK install invocation.curlalso gains--retry 5 --retry-delay 1 --retry-all-errors(so it retries connection resets on its own) and anEXITtrap to clean up the temp file.eng/install-dotnet.ps1: newInvoke-WithRetryhelper wrapsInvoke-WebRequestand the install-script invocation, which now throws on a non-zero exit code so retries actually trigger.Testing
bash -nandpwshsyntax checks pass on both scripts.retryhelper: succeeds when the command eventually passes, gives up after 5 attempts with a non-zero exit otherwise.