Fix stuck dedup candidate, enrich timeout/starvation, and qobuz status error
1. enrich-buy-url.py re-queried every no-match track on every daily run (external, rate-limited lookups over the whole backlog), which blew past its 30-min timeout. Cache "tried, no match" with a BUY_URL_TRIED marker tag and skip those for 30 days (--force still re-checks). This is what made "Look up buy links" fail with TIMEOUT after 1800s. 2. Move maintenance:enrich_buy_url to run last (09:45) in the 9am block, after the fingerprint index (09:25) and status report (09:30). enrich starting at 09:10 and holding the shared lock is what left "Rebuild fingerprint index" skipped_lock. 3. pipeline-status.sh: guard the qobuz/app_id read with a file-exists check. `< missing 2>/dev/null` still leaks the shell's redirection error (opened before 2>/dev/null applies), so the daily report logged "qobuz/app_id: No such file or directory" every run. 4. dedup confirm_and_apply: a candidate whose delete target no longer exists (already removed by an earlier dedup/upgrade/hand) was filtered out and the apply silently no-op'd, leaving it stuck in the queue with no way to delete or clear it (the 100 gecs case). Now mark such candidates applied so a Delete click clears them. Covered by tests/test_dedup_review.py. Tests: 46 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -130,17 +130,19 @@ MAINTENANCE_JOBS: dict[str, tuple[dict, callable]] = {
|
||||
dict(minute=5, hour=9),
|
||||
_lib("maintenance:export_laptop_playlists", "export-laptop-playlists.py"),
|
||||
),
|
||||
"maintenance:enrich_buy_url": (
|
||||
dict(minute=10, hour=9),
|
||||
# 30-min cap: this hits external buy-link APIs per track and has hung
|
||||
# holding the pipeline lock (2026-07-09/10), starving every job after
|
||||
# it. A timeout releases the lock so the rest of the 9am chain runs.
|
||||
_lib("maintenance:enrich_buy_url", "enrich-buy-url.py", ["--apply"], timeout=1800),
|
||||
),
|
||||
"maintenance:build_fingerprint_index": (
|
||||
dict(minute=25, hour=9),
|
||||
_lib("maintenance:build_fingerprint_index", "build-fingerprint-index.py", ["--workers", "8"], timeout=3600),
|
||||
),
|
||||
"maintenance:enrich_buy_url": (
|
||||
# Runs LAST in the 9am block: it hits external buy-link APIs per track
|
||||
# and can run long, so scheduling it after the fingerprint index (09:25)
|
||||
# and the status report (09:30) keeps it from starving them via the
|
||||
# shared lock. The 30-min cap is a backstop; enrich-buy-url.py also now
|
||||
# caches "tried, no match" so it stops re-querying the whole backlog.
|
||||
dict(minute=45, hour=9),
|
||||
_lib("maintenance:enrich_buy_url", "enrich-buy-url.py", ["--apply"], timeout=1800),
|
||||
),
|
||||
"maintenance:pipeline_status_report": (
|
||||
dict(minute=30, hour=9),
|
||||
# Read-only (reads logs, pings Navidrome, sends Telegram). Runs WITHOUT
|
||||
|
||||
Reference in New Issue
Block a user