fix(upgrade): retry download on transient network errors with user feedback
Add downloadWithRetry with up to 3 attempts and quadratic backoff (5s, 20s) to handle TLS timeouts and transient failures. Progress messages inform the user of each failure and wait time before retrying.
This commit is contained in:
parent
29f4886a53
commit
db3e74a736
2 changed files with 38 additions and 1 deletions
|
|
@ -16,6 +16,43 @@ import (
|
|||
|
||||
var httpClient = &http.Client{Timeout: 120 * time.Second}
|
||||
|
||||
const (
|
||||
maxDownloadRetries = 3
|
||||
retryBaseDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
// retryDelays returns the wait duration before the nth retry (1-based).
|
||||
// Delays: 5s, 15s — increasing gap to avoid hammering on transient failures.
|
||||
func retryDelay(attempt int) time.Duration {
|
||||
return retryBaseDelay * time.Duration(attempt*attempt)
|
||||
}
|
||||
|
||||
// downloadWithRetry fetches the release archive, retrying on transient errors.
|
||||
// onProgress is called with user-facing messages (may be nil).
|
||||
func downloadWithRetry(ctx context.Context, version string, onProgress func(string)) (string, error) {
|
||||
var lastErr error
|
||||
for attempt := 1; attempt <= maxDownloadRetries; attempt++ {
|
||||
path, err := download(ctx, version)
|
||||
if err == nil {
|
||||
return path, nil
|
||||
}
|
||||
lastErr = err
|
||||
if attempt < maxDownloadRetries {
|
||||
delay := retryDelay(attempt)
|
||||
if onProgress != nil {
|
||||
onProgress(fmt.Sprintf("Download failed (%v)", err))
|
||||
onProgress(fmt.Sprintf("Retrying in %s... (attempt %d/%d)", delay, attempt+1, maxDownloadRetries))
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
case <-time.After(delay):
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", lastErr
|
||||
}
|
||||
|
||||
// download fetches the release archive to a temporary file.
|
||||
func download(ctx context.Context, version string) (string, error) {
|
||||
url := releaseURL(version, archiveName(version))
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func (u *Upgrader) Execute(ctx context.Context, targetVersion string) Result {
|
|||
|
||||
// 4. Download archive
|
||||
u.log(fmt.Sprintf("Downloading v%s...", targetVersion))
|
||||
archivePath, err := download(ctx, targetVersion)
|
||||
archivePath, err := downloadWithRetry(ctx, targetVersion, u.log)
|
||||
if err != nil {
|
||||
return u.fail("download: %v", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue