fix(downloads): worker claim fairness + bounded job listings (R12 S5)
#2 — _claim_job excluded users at their concurrency limit only in Python, AFTER a fixed LIMIT-50 fetch window. One user flooding the queue with >50 queued jobs while at their cap filled the whole window, chosen came back None, and every other user's work past position 50 was never seen. Exclude capped users IN THE SQL (per-user max_concurrent from the quota row, else the sysconfig default; unlimited users never capped), keeping the Python re-check as a race guard that can only reject, never over-claim. #3 — list_downloads and shared_with_me loaded every job for a user with no bound. Cap both at 500 newest-first (a shared _MAX_LIST, matching the admin listing's existing cap); the Download Center renders newest-first and 500 is far beyond any real manual-download history, so no frontend change is needed. Tests: DB-backed — a capped user's 60-job flood no longer starves a user behind the window; oldest-by-queue_pos claim, all-capped->None, unlimited never excluded; and the list cap bounds the result set.
This commit is contained in:
@@ -40,6 +40,11 @@ admin_router = APIRouter(prefix="/api/admin/downloads", tags=["admin-downloads"]
|
||||
|
||||
_VIDEO_ID = re.compile(r"^[A-Za-z0-9_-]{11}$")
|
||||
|
||||
# Cap the per-user job listings so a heavy library can't return an unbounded result set. The
|
||||
# Download Center renders newest-first; 500 is far beyond any real manual-download history (and
|
||||
# matches the admin listing's own cap).
|
||||
_MAX_LIST = 500
|
||||
|
||||
|
||||
# --- source + serialization ----------------------------------------------------------------
|
||||
|
||||
@@ -334,6 +339,7 @@ def list_downloads(
|
||||
select(DownloadJob)
|
||||
.where(DownloadJob.user_id == user.id)
|
||||
.order_by(DownloadJob.created_at.desc(), DownloadJob.id.desc())
|
||||
.limit(_MAX_LIST)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
@@ -380,6 +386,7 @@ def shared_with_me(
|
||||
.join(DownloadShare, DownloadShare.job_id == DownloadJob.id)
|
||||
.where(DownloadShare.to_user_id == user.id)
|
||||
.order_by(DownloadShare.created_at.desc())
|
||||
.limit(_MAX_LIST)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
@@ -881,7 +888,7 @@ def admin_list_downloads(
|
||||
) -> list[dict]:
|
||||
jobs = (
|
||||
db.execute(
|
||||
select(DownloadJob).order_by(DownloadJob.created_at.desc(), DownloadJob.id.desc()).limit(500)
|
||||
select(DownloadJob).order_by(DownloadJob.created_at.desc(), DownloadJob.id.desc()).limit(_MAX_LIST)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
|
||||
Reference in New Issue
Block a user