Skip to content

'dont_count_slow_torrents' may exceed 'active_downloads' because inactive state is retained after transfer resumes #8579

Description

@cwm9cwm9

Description

When dont_count_slow_torrents is enabled, libtorrent may start additional auto-managed downloads even though an already-running torrent is transferring far above inactive_down_rate.

This is visible in qBittorrent with the following configuration:

  • Maximum active downloads: 1
  • Do not count slow torrents in these limits: enabled
  • Download-rate threshold: 10 KiB/s
  • Torrent inactivity timer: any nonzero value

A torrent downloading continuously at approximately 10 MiB/s should clearly count toward the active-download limit, but another queued torrent is nevertheless started.

Related downstream reports:

  • qBittorrent issue #20647: queueing limit is exceeded while torrents are above the slow threshold
  • qBittorrent issue #21284: enabling “Do not count slow torrents” may start every torrent in the queue

No equivalent current libtorrent issue appears to exist.

Expected behavior

With:

active_downloads = 1
dont_count_slow_torrents = true
inactive_down_rate = 10 KiB/s

the behavior should be:

  1. Torrent A starts downloading.
  2. If Torrent A stays below the inactivity thresholds for the configured interval, it stops counting toward active_downloads.
  3. Torrent B may then start.
  4. As soon as Torrent A resumes transferring above the threshold, Torrent A should count toward active_downloads again.
  5. The auto-manager should restore the configured active-download limit, normally by pausing the lower-priority torrent.

A torrent downloading at several MiB/s should never remain exempt from active_downloads.

Actual behavior

Torrent A may be downloading at approximately 10 MiB/s, while Torrent B is also started despite:

active_downloads = 1
inactive_down_rate = 10 KiB/s

In some cases, many or all queued torrents may start.

Steps to reproduce

  1. Create a libtorrent session with approximately these settings:
settings_pack settings;

settings.set_int(settings_pack::active_downloads, 1);
settings.set_bool(settings_pack::dont_count_slow_torrents, true);
settings.set_int(settings_pack::inactive_down_rate, 10 * 1024);
settings.set_int(settings_pack::inactive_up_rate, 1 * 1024);
settings.set_int(settings_pack::auto_manage_startup, 60);
  1. Add at least two incomplete torrents as auto-managed torrents.
  2. Allow Torrent A to begin downloading.
  3. Verify that Torrent A’s payload download rate remains well above inactive_down_rate, for example 10 MiB/s.
  4. Observe whether Torrent B is also unpaused by the auto-manager.
  5. Optionally begin with Torrent A inactive, then allow it to acquire peers and resume at a high rate.

Suspected cause

The qBittorrent side appears to pass the configured threshold to libtorrent correctly. For example, a configured value of 10 KiB/s is passed as approximately 10,240 bytes/s.

The likely issue is libtorrent’s cached inactive state, m_inactive, and how it interacts with auto-management.

The auto-manager exempts torrents for which torrent::is_inactive() is true from the active_downloads and active_seeds limits. This is expected while a torrent is genuinely inactive.

However, the inactive state appears to be debounced for both state transitions:

active -> inactive
inactive -> active

Using the inactivity interval for the first transition makes sense: a short transfer-rate dip should not immediately free an active slot.

Using the same delay for the reverse transition is problematic. Once an inactive torrent begins transferring above the configured threshold, it can continue to report is_inactive() == true until the inactivity timer expires.

During that interval, the torrent can transfer at a high rate while still being excluded from active_downloads. The auto-manager may consequently start another queued torrent.

There may be a second related issue when dont_count_slow_torrents is enabled or its settings are updated. An auto-managed torrent that is paused because it is queued naturally has a payload rate of zero. If such torrents are evaluated as inactive before they have been allowed to run, they may be preclassified as exempt from the active-download limit. The auto-manager can then unpause multiple queued torrents.

In other words, the problematic sequence may be:

queued torrent
    -> zero transfer rate
    -> classified inactive
    -> exempt from active_downloads
    -> unpaused by auto-manager
    -> transfers above threshold
    -> remains classified inactive during recovery delay
    -> another queued torrent is also started

Suggested fix

1. Clear the inactive state immediately on recovery

The delay should apply when determining that a torrent has become inactive, but not when determining that it has become active again.

Conceptually:

bool const currentlySlow =
    payloadDownloadRate < inactiveDownRate
    && payloadUploadRate < inactiveUpRate;

if (!m_inactive)
{
    if (currentlySlow)
    {
        // Start or continue the inactivity timer.
        // Set m_inactive only after the configured interval.
    }
    else
    {
        // Cancel/reset any pending inactivity timer.
    }
}
else
{
    if (!currentlySlow)
    {
        // Recovery should be immediate.
        m_inactive = false;
        trigger_auto_manage();
    }
}

This preserves protection against brief low-rate periods while ensuring that a torrent transferring above the threshold immediately counts against the queue limits.

2. Do not classify queued/paused torrents as inactive based solely on zero rate

A torrent paused by the auto-manager because it is queued has no meaningful transfer rate. Its zero rate should not cause it to be marked inactive before it has been started.

Possible approaches include:

if (is_paused())
    return false;

or restricting inactivity accounting to torrents that are:

  • auto-managed;
  • currently unpaused;
  • in a downloading or seeding state; and
  • have had an opportunity to transfer for the configured interval.

The exact condition may need to distinguish a user-paused torrent from one paused by auto-management.

3. Re-run auto-management when inactivity is cleared

When m_inactive changes from true to false, auto-management should be triggered promptly so that active_downloads is enforced again.

Otherwise, even after the state is corrected, too many downloads may remain active until some unrelated event causes another auto-management pass.

Suggested regression tests

Recovered torrent counts immediately

  1. Set active_downloads = 1.
  2. Enable dont_count_slow_torrents.
  3. Make Torrent A inactive long enough for Torrent B to start.
  4. Increase Torrent A’s payload rate above inactive_down_rate.
  5. Verify immediately, or on the next management tick, that no more than one torrent remains active.

High-rate torrent never frees its slot

  1. Set active_downloads = 1.
  2. Keep Torrent A above inactive_down_rate.
  3. Wait longer than auto_manage_startup.
  4. Verify that Torrent B remains queued.

Queued torrents are not preclassified inactive

  1. Add several auto-managed incomplete torrents.
  2. Set active_downloads = 1.
  3. Enable dont_count_slow_torrents.
  4. Verify that queued torrents are not all unpaused merely because their current rate is zero.

Brief rate dip does not free a slot

  1. Keep Torrent A below the threshold for less than auto_manage_startup.
  2. Verify that Torrent B remains queued.
  3. Restore Torrent A above the threshold.
  4. Verify that the pending inactivity transition is canceled.

Additional diagnostic suggestion

It may help to log the following during an auto-management pass:

torrent name/hash
paused
auto_managed
payload download rate
payload upload rate
m_inactive
time in candidate inactive state
whether it consumed active_downloads
auto-management action

That should confirm whether the high-rate torrent is still carrying a stale inactive flag when the additional torrent is started.

Environment

Observed through qBittorrent 5.1.x using libtorrent 2.0.x.

The exact libtorrent version can be added from qBittorrent’s:

Help -> About -> Software used

Notes

The source-level cause described above is a hypothesis based on the inactivity and auto-management paths. A minimal libtorrent-only reproduction or temporary logging of m_inactive transitions would be useful to confirm it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions