mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Fix database locked errors (#3153)
* Make read-only operations use WithReadTxn * Allow one database write thread * Add unit test for concurrent transactions * Perform some actions after commit to release txn * Suppress some errors from cancelled context
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fvbommel/sortorder"
|
||||
@@ -73,7 +72,7 @@ type Database struct {
|
||||
|
||||
schemaVersion uint
|
||||
|
||||
writeMu sync.Mutex
|
||||
lockChan chan struct{}
|
||||
}
|
||||
|
||||
func NewDatabase() *Database {
|
||||
@@ -87,6 +86,7 @@ func NewDatabase() *Database {
|
||||
Image: NewImageStore(fileStore),
|
||||
Gallery: NewGalleryStore(fileStore, folderStore),
|
||||
Performer: NewPerformerStore(),
|
||||
lockChan: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -106,8 +106,8 @@ func (db *Database) Ready() error {
|
||||
// necessary migrations must be run separately using RunMigrations.
|
||||
// Returns true if the database is new.
|
||||
func (db *Database) Open(dbPath string) error {
|
||||
db.writeMu.Lock()
|
||||
defer db.writeMu.Unlock()
|
||||
db.lockNoCtx()
|
||||
defer db.unlock()
|
||||
|
||||
db.dbPath = dbPath
|
||||
|
||||
@@ -152,9 +152,36 @@ func (db *Database) Open(dbPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// lock locks the database for writing.
|
||||
// This method will block until the lock is acquired of the context is cancelled.
|
||||
func (db *Database) lock(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case db.lockChan <- struct{}{}:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// lock locks the database for writing. This method will block until the lock is acquired.
|
||||
func (db *Database) lockNoCtx() {
|
||||
db.lockChan <- struct{}{}
|
||||
}
|
||||
|
||||
// unlock unlocks the database
|
||||
func (db *Database) unlock() {
|
||||
// will block the caller if the lock is not held, so check first
|
||||
select {
|
||||
case <-db.lockChan:
|
||||
return
|
||||
default:
|
||||
panic("database is not locked")
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) Close() error {
|
||||
db.writeMu.Lock()
|
||||
defer db.writeMu.Unlock()
|
||||
db.lockNoCtx()
|
||||
defer db.unlock()
|
||||
|
||||
if db.db != nil {
|
||||
if err := db.db.Close(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user