fix(server): encapsulated way to upgrade tx to write mode

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-02-26 21:29:53 -05:00
parent b6cc32a436
commit a5741050df
4 changed files with 20 additions and 10 deletions

View File

@ -42,5 +42,6 @@ type DataStore interface {
Resource(ctx context.Context, model interface{}) ResourceRepository
WithTx(block func(tx DataStore) error, scope ...string) error
WithTxImmediate(block func(tx DataStore) error, scope ...string) error
GC(ctx context.Context) error
}

View File

@ -143,6 +143,20 @@ func (s *SQLStore) WithTx(block func(tx model.DataStore) error, scope ...string)
})
}
func (s *SQLStore) WithTxImmediate(block func(tx model.DataStore) error, scope ...string) error {
ctx := context.Background()
return s.WithTx(func(tx model.DataStore) error {
// Workaround to force the transaction to be upgraded to immediate mode to avoid deadlocks
// See https://berthub.eu/articles/posts/a-brief-post-on-sqlite3-database-locked-despite-timeout/
_ = tx.Property(ctx).Put("tmp_lock_flag", "")
defer func() {
_ = tx.Property(ctx).Delete("tmp_lock_flag")
}()
return block(tx)
}, scope...)
}
func (s *SQLStore) GC(ctx context.Context) error {
trace := func(ctx context.Context, msg string, f func() error) func() error {
return func() error {

View File

@ -6,7 +6,6 @@ import (
"net/http"
"time"
"github.com/google/uuid"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
@ -113,15 +112,7 @@ func (api *Router) setStar(ctx context.Context, star bool, ids ...string) error
return nil
}
event := &events.RefreshResource{}
err := api.ds.WithTx(func(tx model.DataStore) error {
// Workaround to force the transaction to be upgraded to immediate mode to avoid deadlocks
// See https://berthub.eu/articles/posts/a-brief-post-on-sqlite3-database-locked-despite-timeout/
tmpID := uuid.NewString()
_ = tx.Property(ctx).Put("tmp_"+tmpID, "")
defer func() {
_ = tx.Property(ctx).Delete("tmp_" + tmpID)
}()
err := api.ds.WithTxImmediate(func(tx model.DataStore) error {
for _, id := range ids {
exist, err := tx.Album(ctx).Exists(id)
if err != nil {

View File

@ -213,6 +213,10 @@ func (db *MockDataStore) WithTx(block func(tx model.DataStore) error, label ...s
return block(db)
}
func (db *MockDataStore) WithTxImmediate(block func(tx model.DataStore) error, label ...string) error {
return block(db)
}
func (db *MockDataStore) Resource(context.Context, any) model.ResourceRepository {
return struct{ model.ResourceRepository }{}
}