mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-11-10 12:38:32 +00:00
With one global limit, a few tasks that are only waiting hold every slot. A Soulseek download sitting in a peer's queue is the example I hit: three of those and a torrent that could start waits behind them. `MAX_CONCURRENT_DOWNLOADS_PER_SOURCE` (0 keeps the single global limit, which is the default). With it set, the coordinator starts a download only when its source is below the limit and passes over tasks of a full source without losing their place in the queue. `get_next` gets an optional filter for this, and a cancelled task is still dropped when one is given. The start-up stagger now applies only between downloads from the same source, and the pool is sized so every source can reach its limit. The second commit: qBittorrent keeps its own queue of torrents waiting for a free slot. The task polling one of those does no work, so it no longer counts against its source's limit and the next task of that source can start. The client does the pacing, as it does for its own queue. Tests cover the queue pass-over (order and priority kept, nothing eligible, cancelled tasks), the coordinator (a full source doesn't block the others, each source has its own limit, the global limit when unset) and the client-queue case. Ruff and the backend suite pass.
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""A queue read that passes over the tasks its caller cannot start yet."""
|
|
|
|
from shelfmark.core.models import DownloadTask, QueueStatus
|
|
from shelfmark.core.queue import BookQueue
|
|
|
|
|
|
def _task_from(source: str, task_id: str, priority: int = 0) -> DownloadTask:
|
|
return DownloadTask(task_id=task_id, source=source, title=task_id, priority=priority)
|
|
|
|
|
|
def test_get_next_can_pass_over_a_task_and_leave_it_first_in_line():
|
|
queue = BookQueue()
|
|
assert queue.add(_task_from("slskd", "first-soulseek")) is True
|
|
assert queue.add(_task_from("audiobookbay", "torrent")) is True
|
|
assert queue.add(_task_from("slskd", "second-soulseek")) is True
|
|
|
|
picked = queue.get_next(lambda task: task.source != "slskd")
|
|
|
|
assert picked is not None
|
|
assert picked[0] == "torrent"
|
|
# Both Soulseek tasks are back, in their original order.
|
|
assert queue.get_next()[0] == "first-soulseek"
|
|
assert queue.get_next()[0] == "second-soulseek"
|
|
assert queue.get_next() is None
|
|
|
|
|
|
def test_get_next_returns_nothing_when_every_task_is_passed_over_and_keeps_them():
|
|
queue = BookQueue()
|
|
assert queue.add(_task_from("slskd", "a")) is True
|
|
assert queue.add(_task_from("slskd", "b")) is True
|
|
|
|
assert queue.get_next(lambda _task: False) is None
|
|
|
|
assert queue.get_next()[0] == "a"
|
|
assert queue.get_next()[0] == "b"
|
|
|
|
|
|
def test_a_passed_over_task_keeps_its_priority_against_a_later_one():
|
|
queue = BookQueue()
|
|
assert queue.add(_task_from("slskd", "urgent", priority=-10)) is True
|
|
assert queue.add(_task_from("audiobookbay", "normal", priority=0)) is True
|
|
|
|
assert queue.get_next(lambda task: task.source != "slskd")[0] == "normal"
|
|
assert queue.get_next()[0] == "urgent"
|
|
|
|
|
|
def test_a_cancelled_task_is_dropped_even_when_a_filter_is_given():
|
|
queue = BookQueue()
|
|
assert queue.add(_task_from("audiobookbay", "gone")) is True
|
|
queue.update_status("gone", QueueStatus.CANCELLED)
|
|
|
|
assert queue.get_next(lambda _task: True) is None
|