From 359ea3c61cda4781f91dc48e882c15170996f417 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 23 Mar 2026 20:25:31 -0400 Subject: [PATCH] refactor(playlists): improve evaluator efficiency and logging - Use time.NewTimer instead of time.After to avoid timer leaks - Create admin context once per batch instead of per playlist - Pass context to log calls for consistent request-scoped fields --- core/playlists/evaluator.go | 26 ++++++++++++-------------- scanner/phase_4_playlists.go | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/core/playlists/evaluator.go b/core/playlists/evaluator.go index 0b237c936..d0d3463ca 100644 --- a/core/playlists/evaluator.go +++ b/core/playlists/evaluator.go @@ -69,31 +69,29 @@ func (e *smartPlaylistEvaluator) run() { } func (e *smartPlaylistEvaluator) waitSignal(timeout time.Duration) { + timer := time.NewTimer(timeout) + defer timer.Stop() select { - case <-time.After(timeout): + case <-timer.C: case <-e.wakeSignal: } } func (e *smartPlaylistEvaluator) processBatch(batch []string) { - log.Debug("Evaluating smart playlists in background", "count", len(batch)) - for _, id := range batch { - e.doEvaluate(id) - } -} - -func (e *smartPlaylistEvaluator) doEvaluate(id string) { // Use admin context so userFilter() returns all playlists. // Evaluate() internally uses pls.OwnerID for annotation JOINs. ctx := auth.WithAdminUser(context.TODO(), e.ds) - start := time.Now() - err := e.ds.Playlist(ctx).Evaluate(id) - if err != nil { - log.Error("Error evaluating smart playlist in background", "id", id, err) - return + log.Debug(ctx, "Evaluating smart playlists in background", "count", len(batch)) + for _, id := range batch { + start := time.Now() + err := e.ds.Playlist(ctx).Evaluate(id) + if err != nil { + log.Error(ctx, "Error evaluating smart playlist in background", "id", id, err) + continue + } + log.Debug(ctx, "Smart playlist evaluation complete", "id", id, "elapsed", time.Since(start)) } - log.Debug("Background smart playlist evaluation complete", "id", id, "elapsed", time.Since(start)) } // NoopSmartPlaylistEvaluator returns an evaluator that does nothing. diff --git a/scanner/phase_4_playlists.go b/scanner/phase_4_playlists.go index e3d5f16d8..3c8c6aedc 100644 --- a/scanner/phase_4_playlists.go +++ b/scanner/phase_4_playlists.go @@ -109,7 +109,7 @@ func (p *phasePlaylists) processPlaylistsInFolder(folder *model.Folder) (*model. } if pls.IsSmartPlaylist() { p.spe.Enqueue(pls.ID) - log.Debug("Scanner: Imported smart playlist", "name", pls.Name, "lastUpdated", pls.UpdatedAt, "path", pls.Path, "elapsed", time.Since(started)) + log.Debug(p.ctx, "Scanner: Imported smart playlist", "name", pls.Name, "lastUpdated", pls.UpdatedAt, "path", pls.Path, "elapsed", time.Since(started)) } else { log.Debug("Scanner: Imported playlist", "name", pls.Name, "lastUpdated", pls.UpdatedAt, "path", pls.Path, "numTracks", len(pls.Tracks), "elapsed", time.Since(started)) }