diff --git a/persistence/plugin_repository.go b/persistence/plugin_repository.go index c1e36f0b1..7d5781f49 100644 --- a/persistence/plugin_repository.go +++ b/persistence/plugin_repository.go @@ -35,6 +35,14 @@ func (r *pluginRepository) ClearErrors() error { if !r.isPermitted() { return rest.ErrPermissionDenied } + // An UPDATE takes the write lock even when nothing matches, so only run it when there is an error to clear + var hasErrors bool + if err := r.db.NewQuery("SELECT EXISTS (SELECT 1 FROM plugin WHERE last_error != '')").Row(&hasErrors); err != nil { + return err + } + if !hasErrors { + return nil + } _, err := r.db.NewQuery("UPDATE plugin SET last_error = '' WHERE last_error != ''").Execute() return err } diff --git a/persistence/plugin_repository_test.go b/persistence/plugin_repository_test.go index dc68b0892..9b135057e 100644 --- a/persistence/plugin_repository_test.go +++ b/persistence/plugin_repository_test.go @@ -1,7 +1,10 @@ package persistence import ( + "context" + "github.com/deluan/rest" + "github.com/navidrome/navidrome/db" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/request" . "github.com/onsi/ginkgo/v2" @@ -198,6 +201,18 @@ var _ = Describe("PluginRepository", func() { err := repo.ClearErrors() Expect(err).To(BeNil()) }) + + It("does not need the write lock when no plugins have errors", func() { + _ = repo.Put(&model.Plugin{ID: "clean-plugin", Path: "/plugins/c.wasm", Manifest: "{}", SHA256: "h1"}) + conn, err := db.Db().Conn(GinkgoT().Context()) + Expect(err).ToNot(HaveOccurred()) + DeferCleanup(conn.Close) + _, err = conn.ExecContext(GinkgoT().Context(), "BEGIN IMMEDIATE") + Expect(err).ToNot(HaveOccurred()) + DeferCleanup(func() { _, _ = conn.ExecContext(context.Background(), "ROLLBACK") }) + + Expect(repo.ClearErrors()).To(Succeed()) + }) }) })