BUG: getDownloadsByClientType executes sequential blocking awaits on download clients instead of parallel promises #35

Closed
opened 2026-05-22 19:10:31 +01:00 by Gandalf · 1 comment
Owner

Problem Location

  • File: server/utils/downloadClients.js
  • Lines: 148-173 (within getDownloadsByClientType)
  • Affected Downstream: server/utils/poller.js (line 112)

Issue Details

In DownloadClientRegistry.getDownloadsByClientType(), the method retrieves all configured download clients and iterates over them sequentially using a for (const client of clients) loop:

    for (const client of clients) {
      const type = client.getClientType();
      ...
      try {
        const downloads = await client.getActiveDownloads();
        result[type].push(...downloads);
      } catch (error) {
        ...
      }
    }

Because of the sequential await client.getActiveDownloads();, if one client is slow, unresponsive, or experiencing network/timeout issues, the entire polling cycle is blocked. If multiple clients timeout, the latency accumulates linearly.

In contrast, getAllDownloads() correctly uses Promise.allSettled to query all clients in parallel and then aggregates the results, which prevents a single slow client from stalling the entire system.

Because the main background poller at server/utils/poller.js relies directly on getDownloadsByClientType() to update active downloads, this sequential behavior has a severe performance impact on the dashboard, making real-time SSE updates sluggish or completely stalled.

Proposed Fix

Refactor getDownloadsByClientType() to query all clients concurrently using Promise.allSettled, and then group the resolved downloads by client type.

### Problem Location - **File**: `server/utils/downloadClients.js` - **Lines**: 148-173 (within `getDownloadsByClientType`) - **Affected Downstream**: `server/utils/poller.js` (line 112) ### Issue Details In `DownloadClientRegistry.getDownloadsByClientType()`, the method retrieves all configured download clients and iterates over them sequentially using a `for (const client of clients)` loop: ```javascript for (const client of clients) { const type = client.getClientType(); ... try { const downloads = await client.getActiveDownloads(); result[type].push(...downloads); } catch (error) { ... } } ``` Because of the sequential `await client.getActiveDownloads();`, if one client is slow, unresponsive, or experiencing network/timeout issues, the entire polling cycle is blocked. If multiple clients timeout, the latency accumulates linearly. In contrast, `getAllDownloads()` correctly uses `Promise.allSettled` to query all clients in parallel and then aggregates the results, which prevents a single slow client from stalling the entire system. Because the main background poller at `server/utils/poller.js` relies directly on `getDownloadsByClientType()` to update active downloads, this sequential behavior has a severe performance impact on the dashboard, making real-time SSE updates sluggish or completely stalled. ### Proposed Fix Refactor `getDownloadsByClientType()` to query all clients concurrently using `Promise.allSettled`, and then group the resolved downloads by client type.
Gandalf added the Kind/Bug label 2026-05-22 19:16:27 +01:00
Author
Owner

Resolved via commit 12c44a6 (parallel polling).

Resolved via commit 12c44a6 (parallel polling).
Gandalf added the Area/Download Clients label 2026-05-28 11:53:52 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Gandalf/sofarr#35