From ae36e4dfc72299a58b0f9568bbda1dedb6603584 Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 17 Jul 2026 14:13:09 -0400 Subject: [PATCH] fix(artwork): recompute on original serves when the image cache is disabled With ImageCacheSize=0 every serve reads live bytes, so an in-place cover swap without rescan changed the served image with no signal the worker could see. Original-size serves now force on disabled caches, and a new unchanged-hash guard skips the DB write, so the forced path costs only the background decode those installs already pay per request. --- core/artwork/artwork.go | 8 +++++--- core/artwork/blurhash_updater.go | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/artwork/artwork.go b/core/artwork/artwork.go index 93e117168..0b9b8e3a4 100644 --- a/core/artwork/artwork.go +++ b/core/artwork/artwork.go @@ -90,9 +90,11 @@ func (a *artwork) Get(ctx context.Context, artID model.ArtworkID, size int, squa if a.blurHashes != nil { // An original-size miss on an operational cache means a new/changed image even when no // entity row moved. Resized misses don't qualify (their keys vary per size, and their - // readers re-fetch the original through Get, carrying the real signal); nor does cache - // warmup/disabled, where every serve misses — there the LastUpdated signal applies. - force := size == 0 && !square && !r.Cached && a.cache.Available(ctx) + // readers re-fetch the original through Get, carrying the real signal). With the cache + // permanently disabled every serve reads live bytes, so original serves always force + // (the worker's unchanged-hash guard keeps that write-free); warmup forces nothing. + force := size == 0 && !square && + ((!r.Cached && a.cache.Available(ctx)) || a.cache.Disabled(ctx)) a.blurHashes.Enqueue(artID, artReader.LastUpdated(), force, false) } return r, artReader.LastUpdated(), nil diff --git a/core/artwork/blurhash_updater.go b/core/artwork/blurhash_updater.go index fa0a75624..2ca56e56b 100644 --- a/core/artwork/blurhash_updater.go +++ b/core/artwork/blurhash_updater.go @@ -198,6 +198,11 @@ func (u *blurHashUpdater) process(ctx context.Context, artID model.ArtworkID, re } return } + // Unchanged hash with an unmoved signal needs no write — keeps forced recomputes (e.g. every + // original serve on cache-disabled installs) from hammering the DB. + if hash == stored && storedAt != nil && !sig.After(*storedAt) { + return + } if err := u.persist(ctx, artID, hash, sig); err != nil { log.Warn(ctx, "BlurHash: error persisting", "artID", artID, err) return