Skip to content

Recycle stalled direct download connections#125

Merged
Nat3z merged 2 commits intomainfrom
t3code/6dc36989
Apr 24, 2026
Merged

Recycle stalled direct download connections#125
Nat3z merged 2 commits intomainfrom
t3code/6dc36989

Conversation

@Nat3z
Copy link
Copy Markdown
Owner

@Nat3z Nat3z commented Apr 24, 2026

Summary

  • Add connection-health monitoring for direct downloads and ranged chunks.
  • Detect sustained throughput collapse relative to recent baseline and recycle stalled connections after a cooldown.
  • Abort and restart affected chunk/part streams cleanly so progress can continue instead of hanging.
  • Reset stream handles and propagate a dedicated reconnect signal to avoid treating refreshes as generic failures.

Testing

  • Not run (not requested).
  • Verified the change is isolated to application/src/electron/handlers/handler.ddl.ts.
  • Reviewed reconnect paths for direct file downloads, multipart downloads, and chunk downloads to ensure connection cleanup happens on abort/error/finish.

Summary by CodeRabbit

Bug Fixes

  • Implemented automatic connection health monitoring that detects sustained speed collapse and triggers recovery actions.
  • Downloads now automatically refresh connections and retry when experiencing significant slowdowns, improving overall reliability without user intervention.
  • Enhanced resource management by ensuring file streams are properly closed and response references cleared when downloads are interrupted or aborted.

- add connection health sampling for direct and ranged downloads
- abort and restart stalled chunk requests after sustained slowdown
- clean up streams and ignore refresh-triggered aborts
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
open-game-installer-web Ready Ready Preview, Comment Apr 24, 2026 1:09am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ac1189d-5420-40e1-aa60-233f58041bd9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A connection health monitor is introduced that samples byte-rate over a sliding window, computes baselines from positive samples, and detects sustained slowdowns or near-zero transfer patterns. When degradation is detected, the monitor triggers a refresh that aborts active ranged requests and causes download retries via a new sentinel error. Progress updates feed byte totals to the monitor, and resource cleanup is enhanced for chunk abort handling.

Changes

Cohort / File(s) Summary
Connection Health Monitoring
application/src/electron/handlers/handler.ddl.ts
Introduces throughput monitoring with sliding-window byte-rate sampling, baseline computation, and slowdown detection. Adds refresh logic to abort ranged requests and trigger retries on connection degradation. Includes lifecycle management (persistence timing, recovery limits, cooldown enforcement), new CONNECTION_REFRESH_REQUESTED sentinel error, and enhanced stream/reference cleanup on chunk abort.

Sequence Diagram

sequenceDiagram
    participant Download as Download Process
    participant Monitor as Connection Health Monitor
    participant RangedReqs as Ranged Requests
    participant FileStream as File Stream
    participant RetryLogic as Retry Control

    Download->>Monitor: Feed progress updates (byte totals)
    Monitor->>Monitor: Sample byte-rate over sliding window
    Monitor->>Monitor: Compute baseline from positive samples
    loop Detection Check
        Monitor->>Monitor: Detect sustained slowdown or near-zero behavior
    end
    alt Slowdown Detected
        Monitor->>Monitor: Check online state & enforce persistence/cooldown
        Monitor->>RetryLogic: Trigger refresh (CONNECTION_REFRESH_REQUESTED)
        RetryLogic->>RangedReqs: Abort active ranged requests
        RangedReqs->>FileStream: Close streams & clear references
        RetryLogic->>Download: Retry download immediately
    else Healthy Connection
        Download->>Download: Continue normally
    end
    Download->>Monitor: Dispose on completion/abort/error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops with connection so fleet,
Monitoring bytes at a steady beat,
When networks lag with a slowdown's sting,
A refresh kicks in—swift as a spring!
Fast downloads dance, no more delays,
Happy streams flowing through all our days! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Recycle stalled direct download connections' clearly and specifically describes the main change: introducing connection health monitoring to detect and recover from stalled downloads by recycling connections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/6dc36989

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Nat3z
Copy link
Copy Markdown
Owner Author

Nat3z commented Apr 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae83daf641

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +645 to +646
if (lastError.message === 'CONNECTION_REFRESH_REQUESTED') {
continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Recreate part abort controller before reconnect retry

When executePartDownload requests a reconnect, it aborts part.abortController; that signal stays permanently aborted. In this retry loop, the new CONNECTION_REFRESH_REQUESTED branch immediately continues without replacing part.abortController, so the next axios.get(..., signal: part.abortController.signal) in executePartDownload is already canceled and subsequent attempts fail quickly. This causes multi-part standard downloads to fail instead of recovering whenever the throughput-collapse reconnect path is triggered.

Useful? React with 👍 / 👎.

@Nat3z Nat3z mentioned this pull request Apr 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
application/src/electron/handlers/handler.ddl.ts (1)

765-802: ⚠️ Potential issue | 🟡 Minor

Chunked refresh path swallows the CONNECTION_REFRESH_REQUESTED signal.

For the chunked flows (executeParallelDownloadForPart here, and executeParallelDownload at Lines 1840-1857), onReconnect sets connectionRefreshRequested = true and aborts every chunk's controller — but the chunk abort handlers at Lines 896-905 and Lines 1995-2005 reject with new Error('Aborted') regardless of why the abort happened. The local connectionRefreshRequested flag set here is never read, so:

  • executeParallelDownloadForPart re-throws 'Aborted' from Promise.all.
  • downloadPartWithState's chunk-mode retry loop (Lines 574-615) has no 'CONNECTION_REFRESH_REQUESTED' branch like the standard path at Line 645, so it falls through to await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))).
  • Same applies to executeParallelDownloaddownloadPart retry loop (Lines 1379-1424 vs. the check at Line 1439).

Net effect: chunked refreshes pay an extra 1‑5s exponential backoff per retry on top of the 15s CONNECTION_HEALTH_RECONNECT_COOLDOWN_MS, and the "dedicated reconnect signal" the PR advertises only applies to the non-chunked paths. Either thread the refresh signal down to the chunk rejection or detect it at the Promise.all boundary before re-throwing.

🔧 Sketch of one possible fix (chunked-part path)

Tag each chunk so its abort handler can emit the right error, and mirror the standard-path check in the retry loop:

 interface ChunkState {
   index: number;
   ...
   abortController: AbortController;
+  refreshRequested?: boolean;
   fileStream?: fs.WriteStream;
   ...
 }
       onReconnect: ({ currentSpeed, baselineSpeed }) => {
         if (connectionRefreshRequested || this.status !== 'downloading') return;
         connectionRefreshRequested = true;
         ...
         for (const activeChunk of part.chunks) {
+          activeChunk.refreshRequested = true;
           activeChunk.abortController.abort();
         }
       },
         chunk.abortController.signal.addEventListener('abort', () => {
           ...
-          reject(new Error('Aborted'));
+          reject(
+            new Error(
+              chunk.refreshRequested ? 'CONNECTION_REFRESH_REQUESTED' : 'Aborted'
+            )
+          );
         });
       } catch (error) {
         lastError = error as Error;
         ...
         if (this.status !== 'downloading') throw lastError;
+        if (lastError.message === 'CONNECTION_REFRESH_REQUESTED') {
+          continue;
+        }
         await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
       }

Apply symmetrically in downloadChunk / executeParallelDownload / downloadPart's retry loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@application/src/electron/handlers/handler.ddl.ts` around lines 765 - 802, The
parallel-chunk flow swallows the CONNECTION_REFRESH_REQUESTED signal because
onReconnect sets connectionRefreshRequested and aborts chunk controllers but
chunk abort handlers always reject new Error('Aborted'); change the abort path
so aborted chunks can indicate a refresh: when onReconnect runs set a flag on
the part/chunk (e.g., part.connectionRefreshRequested or
chunk.connectionRefreshRequested) and have downloadChunk /
downloadChunkForPart's abort handlers reject a distinct
Error('CONNECTION_REFRESH_REQUESTED') (or include a property on the Error) when
that flag is set; update the Promise.all boundary in
executeParallelDownloadForPart to detect and rethrow
CONNECTION_REFRESH_REQUESTED (instead of the generic 'Aborted') and mirror the
same check in the retry loops in downloadPartWithState and downloadPart so they
handle CONNECTION_REFRESH_REQUESTED the same way as the non-chunked path (avoid
falling through to the exponential backoff).
🧹 Nitpick comments (1)
application/src/electron/handlers/handler.ddl.ts (1)

782-782: Redundant initial observe() calls.

createConnectionHealthMonitor already sets observedBytes = options.initialBytes at Line 216, so calling connectionHealth.observe(part.downloadedBytes) immediately after creation with the same value is a no-op. Minor, but worth dropping for clarity. These same four sites each have an identical redundant call.

Also applies to: 1066-1066, 1544-1544, 1857-1857

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@application/src/electron/handlers/handler.ddl.ts` at line 782, The call to
connectionHealth.observe(part.downloadedBytes) is redundant because
createConnectionHealthMonitor already initializes observedBytes to
options.initialBytes; remove these immediate post-construction observe calls at
each site (the one shown and the three other occurrences) to avoid no-op
duplication. Locate where createConnectionHealthMonitor is invoked and drop the
immediately-following connectionHealth.observe(...) lines (references:
createConnectionHealthMonitor, connectionHealth.observe, options.initialBytes,
part.downloadedBytes) so the monitor relies on its internal initialBytes
initialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@application/src/electron/handlers/handler.ddl.ts`:
- Around line 765-802: The parallel-chunk flow swallows the
CONNECTION_REFRESH_REQUESTED signal because onReconnect sets
connectionRefreshRequested and aborts chunk controllers but chunk abort handlers
always reject new Error('Aborted'); change the abort path so aborted chunks can
indicate a refresh: when onReconnect runs set a flag on the part/chunk (e.g.,
part.connectionRefreshRequested or chunk.connectionRefreshRequested) and have
downloadChunk / downloadChunkForPart's abort handlers reject a distinct
Error('CONNECTION_REFRESH_REQUESTED') (or include a property on the Error) when
that flag is set; update the Promise.all boundary in
executeParallelDownloadForPart to detect and rethrow
CONNECTION_REFRESH_REQUESTED (instead of the generic 'Aborted') and mirror the
same check in the retry loops in downloadPartWithState and downloadPart so they
handle CONNECTION_REFRESH_REQUESTED the same way as the non-chunked path (avoid
falling through to the exponential backoff).

---

Nitpick comments:
In `@application/src/electron/handlers/handler.ddl.ts`:
- Line 782: The call to connectionHealth.observe(part.downloadedBytes) is
redundant because createConnectionHealthMonitor already initializes
observedBytes to options.initialBytes; remove these immediate post-construction
observe calls at each site (the one shown and the three other occurrences) to
avoid no-op duplication. Locate where createConnectionHealthMonitor is invoked
and drop the immediately-following connectionHealth.observe(...) lines
(references: createConnectionHealthMonitor, connectionHealth.observe,
options.initialBytes, part.downloadedBytes) so the monitor relies on its
internal initialBytes initialization.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a15c96b1-062d-4f20-b8ad-0d6f84d608e7

📥 Commits

Reviewing files that changed from the base of the PR and between 5360237 and ae83daf.

📒 Files selected for processing (1)
  • application/src/electron/handlers/handler.ddl.ts

- Recreate the abort controller when a connection refresh is requested
- Remove redundant connection health observations from download paths
@Nat3z Nat3z merged commit 998e3b3 into main Apr 24, 2026
6 checks passed
@Nat3z Nat3z deleted the t3code/6dc36989 branch April 24, 2026 01:10
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