mirror of
https://github.com/navidrome/navidrome.git
synced 2026-11-14 06:08:46 +01:00
perf(artwork): cap the stale-absent recheck at 100 items per kind per hour (#6007)
* feat(artwork): drip the stale-absent recheck instead of bursting it daily Each hourly housekeeping tick now re-queues at most 100 absent states per kind, oldest attempts first, instead of everything older than 24h at once. External agents see a flat ~100 requests/hour per agent instead of hourly bursts of ~2,000, and the effective recheck interval self-scales with the size of the absent pool (~4 days at 10k absent artists) while small libraries keep the 24h floor. * feat(artwork): trust an absent artwork state for a week before rechecking With the recheck now dripped at 100 items per kind per hour, the 24h floor only governed small libraries, where the drip cap never binds; they still re-asked every agent daily. A 7-day floor cuts that cost 7x and, for large libraries, becomes the binding limit over the drip cycle (~5.7k calls/day instead of ~9.6k at 10k absent artists). Among comparable servers, this is still the second-most-eager recheck: gonic retries misses every 30 days, Jellyfin and Funkwhale never do. * refactor(artwork): state the drip's backpressure contract where it bites Review follow-ups: the recheck limit deliberately caps the *selection*, not the insertions — already-queued rows use up budget, so a stalled drain admits no new work instead of building a recovery burst. Say so in the interface doc, mirror it in the mock by truncating the sorted candidates (matching the SQL's LIMIT-before-ON CONFLICT), and teach `artwork status` and the worker doc the post-drip wording. Also pin the one cmd fixture that still assumed a 24h recheck window.
This commit is contained in:
parent
ffc68e29db
commit
07b6411c0b
9 changed files with 70 additions and 23 deletions
|
|
@ -134,8 +134,9 @@ type ArtworkQueueRepository interface {
|
|||
// EnqueuePreservingBackoff upserts like Enqueue but preserves an existing row's retry_at, so a
|
||||
// request-triggered read-through never resets a failed resolution's backoff.
|
||||
EnqueuePreservingBackoff(items ...ArtworkQueueItem) error
|
||||
// EnqueueStaleAbsent inserts queue rows (priority Recheck) for absent states older than cutoff.
|
||||
EnqueueStaleAbsent(kind Kind, attemptedBefore time.Time) (int64, error)
|
||||
// EnqueueStaleAbsent inserts queue rows (priority Recheck) for absent states older than cutoff, oldest
|
||||
// first; limit caps the selection, so already-queued rows use up budget (backpressure when the drain stalls).
|
||||
EnqueueStaleAbsent(kind Kind, attemptedBefore time.Time, limit int) (int64, error)
|
||||
// EnqueueAllMissing inserts queue rows for all entities with no item_artwork row, at the given priority.
|
||||
EnqueueAllMissing(kind Kind, priority int) (int64, error)
|
||||
// EnqueueIfMissing inserts only for items with no item_artwork row yet.
|
||||
|
|
@ -160,8 +161,8 @@ type ArtworkQueueRepository interface {
|
|||
// CountQueued reports the pending rows matching the kinds and priorities, grouped by both;
|
||||
// an empty filter means every one.
|
||||
CountQueued(kinds []Kind, priorities []int) ([]ArtworkQueueStat, error)
|
||||
// CountAbsent reports the absent states of a kind, and how many of those EnqueueStaleAbsent
|
||||
// would pick up at the given cutoff.
|
||||
// CountAbsent reports the absent states of a kind, and how many are past the given cutoff,
|
||||
// eligible for EnqueueStaleAbsent (which drains them limit rows per call).
|
||||
CountAbsent(kind Kind, attemptedBefore time.Time) (ArtworkAbsentStat, error)
|
||||
// PurgeDangling removes queue rows whose entity no longer exists.
|
||||
PurgeDangling() (int64, error)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue