From 34f6684922d948913896e71955ba99263dd34829 Mon Sep 17 00:00:00 2001 From: Deluan Date: Wed, 29 Jul 2026 21:29:04 -0400 Subject: [PATCH] refactor(config): make the artwork tuning options dev flags ArtworkWorkerConcurrency and ArtworkExternalMaxRPS become DevArtworkWorkerConcurrency and DevArtworkExternalMaxRPS, joining the other DevArtwork* flags. Their defaults should not need tuning, so they do not belong in the documented, user-facing option set. --- conf/configuration.go | 8 ++++---- core/artwork/e2e/acquire_serve_test.go | 2 +- core/artwork/e2e/resolution_harness_test.go | 2 +- core/artwork/gate.go | 2 +- core/artwork/worker.go | 4 ++-- core/artwork/worker_test.go | 2 +- server/subsonic/e2e/subsonic_artwork_test.go | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/conf/configuration.go b/conf/configuration.go index 9d44d33d9..5122b15eb 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -62,8 +62,6 @@ type configOptions struct { ImageCacheSize string AlbumPlayCountMode string EnableArtworkPrecache bool - ArtworkWorkerConcurrency int - ArtworkExternalMaxRPS int AutoImportPlaylists bool DefaultPlaylistPublicVisibility bool PlaylistsPath string @@ -146,6 +144,8 @@ type configOptions struct { DevArtworkThrottleBacklogLimit int DevArtworkThrottleBacklogTimeout time.Duration DevArtworkThrottleBuffered bool + DevArtworkWorkerConcurrency int + DevArtworkExternalMaxRPS int DevArtistInfoTimeToLive time.Duration DevAlbumInfoTimeToLive time.Duration DevExternalScanner bool @@ -1075,10 +1075,10 @@ func setViperDefaults() { viper.SetDefault("devartworkthrottlebuffered", true) // Half the CPU count (min 2), so local resolution scales with the host but stays under the // SQLite pool (MaxOpenConns) — leaving connections for the scanner, scrobbles and the UI. - viper.SetDefault("artworkworkerconcurrency", max(2, runtime.NumCPU()/2)) + viper.SetDefault("devartworkworkerconcurrency", max(2, runtime.NumCPU()/2)) // External RPS gates outbound calls to third-party services (per service); it is bounded by // their tolerance, not the host, so it stays a small constant regardless of CPU count. - viper.SetDefault("artworkexternalmaxrps", 2) + viper.SetDefault("devartworkexternalmaxrps", 2) viper.SetDefault("devartistinfotimetolive", consts.ArtistInfoTimeToLive) viper.SetDefault("devalbuminfotimetolive", consts.AlbumInfoTimeToLive) viper.SetDefault("devexternalscanner", true) diff --git a/core/artwork/e2e/acquire_serve_test.go b/core/artwork/e2e/acquire_serve_test.go index 4e2540ead..426e65281 100644 --- a/core/artwork/e2e/acquire_serve_test.go +++ b/core/artwork/e2e/acquire_serve_test.go @@ -65,7 +65,7 @@ var _ = Describe("Acquisition → serve loop", func() { conf.Server.CoverArtPriority = "cover.jpg" conf.Server.ArtistArtPriority = "artist.png" // keeps artist resolution offline conf.Server.EnableMediaFileCoverArt = true - conf.Server.ArtworkWorkerConcurrency = 1 + conf.Server.DevArtworkWorkerConcurrency = 1 folderRepo = &fakeFolderRepo{} libRepo = &tests.MockLibraryRepo{} diff --git a/core/artwork/e2e/resolution_harness_test.go b/core/artwork/e2e/resolution_harness_test.go index e8e980909..4021f9ef6 100644 --- a/core/artwork/e2e/resolution_harness_test.go +++ b/core/artwork/e2e/resolution_harness_test.go @@ -90,7 +90,7 @@ func setupResolutionHarness() { conf.Server.ImageCacheSize = "0" conf.Server.EnableExternalServices = false conf.Server.EnableMediaFileCoverArt = true - conf.Server.ArtworkWorkerConcurrency = 1 + conf.Server.DevArtworkWorkerConcurrency = 1 rctx = request.WithUser(GinkgoT().Context(), model.User{ID: "admin-1", UserName: "admin", IsAdmin: true}) harness.TruncateDB(userTables) diff --git a/core/artwork/gate.go b/core/artwork/gate.go index 096b1120d..a64c11a20 100644 --- a/core/artwork/gate.go +++ b/core/artwork/gate.go @@ -67,7 +67,7 @@ func (w *Worker) gateFor(name string) *extGate { if g, ok := w.gates[name]; ok { return g } - rps := conf.Server.ArtworkExternalMaxRPS + rps := conf.Server.DevArtworkExternalMaxRPS limit := rate.Inf if rps > 0 { limit = rate.Limit(rps) diff --git a/core/artwork/worker.go b/core/artwork/worker.go index 499f407d8..115c3ce87 100644 --- a/core/artwork/worker.go +++ b/core/artwork/worker.go @@ -71,9 +71,9 @@ func NewWorker(ds model.DataStore, store *ImageStore, ag *agents.Agents, ffmpeg // rate-limit permit, so a sleeping lookup would crowd out a cover sitting on disk. func newDrainPools() []*drainPool { budget := conf.MaxOpenConns() // floored at 4, so both remainders below stay positive - local := min(max(1, conf.Server.ArtworkWorkerConcurrency), budget-1) + local := min(max(1, conf.Server.DevArtworkWorkerConcurrency), budget-1) // More external slots than the rate allows would only sleep in the limiter. - external := min(max(2, 2*conf.Server.ArtworkExternalMaxRPS), budget-local) + external := min(max(2, 2*conf.Server.DevArtworkExternalMaxRPS), budget-local) return []*drainPool{ {name: "local", kinds: localDrainKinds, concurrency: local, wake: make(chan struct{}, 1)}, {name: "external", kinds: externalDrainKinds, concurrency: external, wake: make(chan struct{}, 1)}, diff --git a/core/artwork/worker_test.go b/core/artwork/worker_test.go index e2aa8bf82..5a282ccee 100644 --- a/core/artwork/worker_test.go +++ b/core/artwork/worker_test.go @@ -145,7 +145,7 @@ var _ = Describe("Worker", func() { ds.MockedAlbum = tests.CreateMockAlbumRepo() store = NewImageStore(GinkgoT().TempDir()) conf.Server.CoverArtPriority = "cover.jpg, embedded" - conf.Server.ArtworkExternalMaxRPS = 1000 // keep the limiter out of the way of behavior tests + conf.Server.DevArtworkExternalMaxRPS = 1000 // keep the limiter out of the way of behavior tests broker = &fakeEventBroker{} imgCache = &recordingCache{FileCache: cache.NewFileCache("WorkerTest", "100MB", "images", 0, func(ctx context.Context, arg cache.Item) (io.Reader, error) { diff --git a/server/subsonic/e2e/subsonic_artwork_test.go b/server/subsonic/e2e/subsonic_artwork_test.go index e757ebf14..70bb02702 100644 --- a/server/subsonic/e2e/subsonic_artwork_test.go +++ b/server/subsonic/e2e/subsonic_artwork_test.go @@ -93,7 +93,7 @@ var _ = Describe("Artwork Serving", Ordered, func() { conf.Server.CoverArtPriority = "cover.jpg" conf.Server.ArtistArtPriority = "artist.png" // offline: artists resolve absent, out of scope here conf.Server.EnableMediaFileCoverArt = false - conf.Server.ArtworkWorkerConcurrency = 1 + conf.Server.DevArtworkWorkerConcurrency = 1 conf.Server.CacheFolder = conf.NewDir(GinkgoT().TempDir()) conf.Server.EnableSharing = true conf.Server.DevArtworkMaxRequests = 100