fix(plugins): skip the startup error reset when no plugin has an error (#6223)

The plugin manager clears last_error on every startup with an UPDATE that takes the SQLite write lock even when no row matches. On slow storage the startup scan often holds the lock at that moment, so the reset waited out the busy timeout and logged "database is locked", even with no plugins installed. ClearErrors now checks for errors with a read first and only writes when there is something to clear.
This commit is contained in:
Deluan Quintão 2026-09-25 12:19:33 -04:00 • committed by GitHub
commit 336fa482f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 0 deletions

View file

@ -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
}

View file

@ -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())
})
})
})