Skip to content

resilient network disconnect#540

Open
SuperCoolPencil wants to merge 4 commits into
mainfrom
resilient-network-disconnect
Open

resilient network disconnect#540
SuperCoolPencil wants to merge 4 commits into
mainfrom
resilient-network-disconnect

Conversation

@SuperCoolPencil

@SuperCoolPencil SuperCoolPencil commented Jul 17, 2026

Copy link
Copy Markdown
Member
  • feat: implement resilient network recovery with transient error detection, state snapshotting, and automatic retry logic
  • feat: add specialized retry logic and exponential backoff for network-related download failures
  • test: add unit tests for worker retry logic and progress persistence on error
  • refactor: remove network retry logic and stabilize failure handling in scheduler

Greptile Summary

This PR adds recovery behavior for transient network failures. The main changes are:

  • Classifies transient network errors and probes for restored connectivity.
  • Adds worker retries, exponential backoff, and connection invalidation.
  • Saves resumable progress when a download fails.
  • Changes scheduler handling for network and generic failures.
  • Adds focused tests for classification, retries, and snapshots.

Confidence Score: 2/5

The recovery path can persist an incomplete task list and then remove the download without requeuing it.

  • The failed byte range can be absent from the saved resume state.
  • Network failures bypass the scheduler's only requeue branch.
  • Permanent DNS failures can spend several minutes in the transient retry loop.

internal/strategy/concurrent/downloader.go, internal/scheduler/scheduler.go, and internal/strategy/concurrent/network_errors.go

T-Rex T-Rex Logs

What T-Rex did

  • I reproduced the range-missing failure by exercising the real Download worker through 19 transient GET retries, after which the persisted DownloadRecord contained only the 4096–8191 range and reported Downloaded as 4096, leaving the 0–4095 range zero-filled on resume.
  • I verified that triggering an ErrNetworkFailure via the RunDownload trampoline produced an orphaned task that remained untracked, with no active or queued task, an empty queue order, no GetStatus result, and progress staying nonterminal.
  • I reproduced that a permanent DNS error can be misinterpreted by the real classifier, where IsNotFound was true,Temporary=false, Timeout=false, yet isTransientNetworkError returned true.
  • I completed the requested verification, but local artifact references were not uploaded.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
internal/strategy/concurrent/downloader.go Adds error snapshots, but the snapshot can omit the byte range that produced the fatal error.
internal/scheduler/scheduler.go Adds network retry state, but network failures bypass the scheduler's only requeue path.
internal/scheduler/manager.go Suppresses terminal error events for network failures so resumable state remains available.
internal/strategy/concurrent/worker.go Adds a separate network retry budget, connectivity waits, mirror rotation, and pool invalidation.
internal/strategy/concurrent/network_errors.go Adds network error classification and connectivity probes, but treats all net.OpError values as transient.
internal/transport/network.go Adds synchronized invalidation of idle connections across managed transports.
internal/types/errors.go Adds the sentinel used to propagate transient network failures.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant W as Worker
participant D as Downloader
participant E as Event Worker
participant S as Scheduler
W->>W: Exhaust network retries
W->>W: Remove failed task from activeTasks
W-->>D: Return transient error
D->>D: Build snapshot from queue and active tasks
Note over D: Failed byte range is absent
D->>E: Persist paused snapshot
D-->>S: Return ErrNetworkFailure
S->>S: Remove active download
Note over S: Network error does not enter requeue branch
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant W as Worker
participant D as Downloader
participant E as Event Worker
participant S as Scheduler
W->>W: Exhaust network retries
W->>W: Remove failed task from activeTasks
W-->>D: Return transient error
D->>D: Build snapshot from queue and active tasks
Note over D: Failed byte range is absent
D->>E: Persist paused snapshot
D-->>S: Return ErrNetworkFailure
S->>S: Remove active download
Note over S: Network error does not enter requeue branch
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
internal/strategy/concurrent/downloader.go:671-672
**Failed Range Missing From Snapshot**

When a worker exhausts its retries, it removes its task from `activeTasks` before returning the error, and that task is no longer in the queue. This snapshot therefore omits the range that caused the failure, overstates `Downloaded`, and can resume without ever downloading the missing bytes.

### Issue 2 of 3
internal/scheduler/scheduler.go:717-718
**Network Failure Skips Requeue**

When `RunDownload` returns `ErrNetworkFailure`, this condition excludes it from the scheduler's only requeue branch, while `networkRetries` is never used elsewhere. The task is removed from active tracking without being queued or marked errored, so an exhausted network retry silently leaves the saved download orphaned.

### Issue 3 of 3
internal/strategy/concurrent/network_errors.go:53-55
**Permanent DNS Errors Become Transient**

Every `net.OpError` is classified as transient, including one wrapping a permanent DNS result such as an unknown host. A download with a misspelled or removed hostname therefore enters the ten-attempt connectivity loop instead of failing normally, delaying the actionable error for several minutes.

Reviews (1): Last reviewed commit: "refactor: remove network retry logic and..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

@github-actions

Copy link
Copy Markdown

Binary Size Analysis

⚠️ Size Increased

Version Human Readable Raw Bytes
Main 17.58 MB 18436388
PR 17.59 MB 18448676
Difference 12.00 KB 12288

@github-actions

Copy link
Copy Markdown

❌ Test Failures on macos-latest

  • github.com/SurgeDM/Surge/internal/strategy/concurrent: TestWorkerRetryOnTransientError

@github-actions

Copy link
Copy Markdown

❌ Test Failures on ubuntu-latest

  • github.com/SurgeDM/Surge/internal/strategy/concurrent: TestWorkerRetryOnTransientError

@github-actions

Copy link
Copy Markdown

❌ Test Failures on windows-latest

  • github.com/SurgeDM/Surge/internal/strategy/concurrent: TestWorkerRetryOnTransientError

Comment on lines +671 to +672
remainingTasks := queue.DrainRemaining()
remainingTasks = append(remainingTasks, activeRemaining...)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Failed Range Missing From Snapshot

When a worker exhausts its retries, it removes its task from activeTasks before returning the error, and that task is no longer in the queue. This snapshot therefore omits the range that caused the failure, overstates Downloaded, and can resume without ever downloading the missing bytes.

Rule Used: What: All code changes must include tests for edge... (source)

Artifacts

Repro: focused Go test driving worker failure, persistence, state reload, and resume readback

  • Contains supporting evidence from the run (text/x-go; charset=utf-8).

Repro: verbose failing test output with serialized DownloadRecord and missing-range readback

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/strategy/concurrent/downloader.go
Line: 671-672

Comment:
**Failed Range Missing From Snapshot**

When a worker exhausts its retries, it removes its task from `activeTasks` before returning the error, and that task is no longer in the queue. This snapshot therefore omits the range that caused the failure, overstates `Downloaded`, and can resume without ever downloading the missing bytes.

**Rule Used:** What: All code changes must include tests for edge... ([source](https://app.greptile.com/surge-org-3/-/custom-context?memory=2b22782d-3452-4d55-b059-e631b2540ce8))

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +717 to +718
isNetworkErr := errors.Is(err, types.ErrNetworkFailure)
canRetryGeneric := !isNetworkErr && qt.retries < 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Network Failure Skips Requeue

When RunDownload returns ErrNetworkFailure, this condition excludes it from the scheduler's only requeue branch, while networkRetries is never used elsewhere. The task is removed from active tracking without being queued or marked errored, so an exhausted network retry silently leaves the saved download orphaned.

Rule Used: What: All code changes must include tests for edge... (source)

Artifacts

Repro: focused Go scheduler harness that drives the ErrNetworkFailure branch

  • Contains supporting evidence from the run (text/x-go; charset=utf-8).

Repro: verbose focused test output and failure trace

  • Keeps the command output available without making the summary code-heavy.

Repro: concise scheduler state and progress-event capture

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/scheduler/scheduler.go
Line: 717-718

Comment:
**Network Failure Skips Requeue**

When `RunDownload` returns `ErrNetworkFailure`, this condition excludes it from the scheduler's only requeue branch, while `networkRetries` is never used elsewhere. The task is removed from active tracking without being queued or marked errored, so an exhausted network retry silently leaves the saved download orphaned.

**Rule Used:** What: All code changes must include tests for edge... ([source](https://app.greptile.com/surge-org-3/-/custom-context?memory=2b22782d-3452-4d55-b059-e631b2540ce8))

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +53 to +55
if errors.As(err, &opErr) {
return true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Permanent DNS Errors Become Transient

Every net.OpError is classified as transient, including one wrapping a permanent DNS result such as an unknown host. A download with a misspelled or removed hostname therefore enters the ten-attempt connectivity loop instead of failing normally, delaying the actionable error for several minutes.

Rule Used: What: All code changes must include tests for edge... (source)

Artifacts

Repro: focused deterministic Go test constructing the permanent DNS error and invoking the real classifier

  • Contains supporting evidence from the run (text/x-go; charset=utf-8).

Repro: verbose test output showing the permanent DNS properties, true classification result, failed assertion, command, working directory, and exit code

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/strategy/concurrent/network_errors.go
Line: 53-55

Comment:
**Permanent DNS Errors Become Transient**

Every `net.OpError` is classified as transient, including one wrapping a permanent DNS result such as an unknown host. A download with a misspelled or removed hostname therefore enters the ten-attempt connectivity loop instead of failing normally, delaying the actionable error for several minutes.

**Rule Used:** What: All code changes must include tests for edge... ([source](https://app.greptile.com/surge-org-3/-/custom-context?memory=2b22782d-3452-4d55-b059-e631b2540ce8))

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant