From 97083114fbfd42f2178fd35750b97e3fbef926b7 Mon Sep 17 00:00:00 2001 From: heimoshuiyu Date: Wed, 15 Dec 2021 02:53:41 +0800 Subject: [PATCH] Add: singleThreadLock for sqlite performance, and change Db.Tag method --- pkg/api/handle_tag.go | 18 ++++++++----- pkg/database/database.go | 3 +++ pkg/database/method.go | 41 +++++++++++++++++++++++++++++ pkg/database/method_feedback.go | 9 +++++++ pkg/database/method_review.go | 18 +++++++++++++ pkg/database/method_tag.go | 36 ++++++++++++++++++------- pkg/database/method_tag_and_file.go | 9 +++++++ pkg/database/method_user.go | 28 ++++++++++++++++++++ 8 files changed, 146 insertions(+), 16 deletions(-) diff --git a/pkg/api/handle_tag.go b/pkg/api/handle_tag.go index 2070e7c..302b4af 100644 --- a/pkg/api/handle_tag.go +++ b/pkg/api/handle_tag.go @@ -54,7 +54,13 @@ func (api *API) HandleInsertTag(w http.ResponseWriter, r *http.Request) { return } - tag, err := api.Db.InsertTag(req) + tagID, err := api.Db.InsertTag(req) + if err != nil { + api.HandleError(w, r, err) + return + } + + tag, err := api.Db.GetTag(tagID) if err != nil { api.HandleError(w, r, err) return @@ -114,16 +120,14 @@ func (api *API) HandleUpdateTag(w http.ResponseWriter, r *http.Request) { api.HandleError(w, r, err) return } - tag, err := api.Db.UpdateTag(req) - if err != nil { - api.HandleError(w, r, err) - return - } - err = json.NewEncoder(w).Encode(tag) + + err = api.Db.UpdateTag(req) if err != nil { api.HandleError(w, r, err) return } + + api.HandleOK(w, r) } type DeleteTagRequest struct { diff --git a/pkg/database/database.go b/pkg/database/database.go index ceb27df..564cc6a 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -2,6 +2,7 @@ package database import ( "database/sql" + "sync" _ "github.com/mattn/go-sqlite3" ) @@ -9,6 +10,7 @@ import ( type Database struct { sqlConn *sql.DB stmt *Stmt + singleThreadLock *sync.Mutex } func NewDatabase(dbName string) (*Database, error) { @@ -30,6 +32,7 @@ func NewDatabase(dbName string) (*Database, error) { database := &Database{ sqlConn: sqlConn, stmt: stmt, + singleThreadLock: &sync.Mutex{}, } return database, nil diff --git a/pkg/database/method.go b/pkg/database/method.go index febe89f..819c8b4 100644 --- a/pkg/database/method.go +++ b/pkg/database/method.go @@ -8,6 +8,9 @@ import ( ) func (database *Database) GetRandomFiles(limit int64) ([]File, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getRandomFiles.Query(limit) if err != nil { return nil, err @@ -28,6 +31,9 @@ func (database *Database) GetRandomFiles(limit int64) ([]File, error) { } func (database *Database) GetRandomFilesWithTag(tagID, limit int64) ([]File, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getRandomFilesWithTag.Query(tagID, limit) if err != nil { return nil, err @@ -48,6 +54,9 @@ func (database *Database) GetRandomFilesWithTag(tagID, limit int64) ([]File, err } func (database *Database) GetFilesInFolder(folder_id int64, limit int64, offset int64) ([]File, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getFilesInFolder.Query(folder_id, limit, offset) if err != nil { return nil, err @@ -69,6 +78,9 @@ func (database *Database) GetFilesInFolder(folder_id int64, limit int64, offset } func (database *Database) SearchFolders(foldername string, limit int64, offset int64) ([]Folder, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.searchFolders.Query("%"+foldername+"%", limit, offset) if err != nil { return nil, errors.New("Error searching folders at query " + err.Error()) @@ -92,6 +104,10 @@ func (database *Database) GetFile(id int64) (*File, error) { file := &File{ Db: database, } + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + err := database.stmt.getFile.QueryRow(id).Scan(&file.ID, &file.Folder_id, &file.Filename, &file.Foldername, &file.Filesize) if err != nil { return nil, err @@ -178,6 +194,10 @@ func (database *Database) GetFolder(folderId int64) (*Folder, error) { folder := &Folder{ Db: database, } + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + err := database.stmt.getFolder.QueryRow(folderId).Scan(&folder.Folder) if err != nil { return nil, err @@ -186,6 +206,9 @@ func (database *Database) GetFolder(folderId int64) (*Folder, error) { } func (database *Database) SearchFiles(filename string, limit int64, offset int64) ([]File, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.searchFiles.Query("%"+filename+"%", limit, offset) if err != nil { return nil, errors.New("Error searching files at query " + err.Error()) @@ -210,6 +233,10 @@ func (database *Database) SearchFiles(filename string, limit int64, offset int64 func (database *Database) FindFolder(folder string) (int64, error) { var id int64 + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + err := database.stmt.findFolder.QueryRow(folder).Scan(&id) if err != nil { return 0, err @@ -219,6 +246,10 @@ func (database *Database) FindFolder(folder string) (int64, error) { func (database *Database) FindFile(folderId int64, filename string) (int64, error) { var id int64 + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + err := database.stmt.findFile.QueryRow(folderId, filename).Scan(&id) if err != nil { return 0, err @@ -227,6 +258,10 @@ func (database *Database) FindFile(folderId int64, filename string) (int64, erro } func (database *Database) InsertFolder(folder string) (int64, error) { + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.insertFolder.Exec(folder, filepath.Base(folder)) if err != nil { return 0, err @@ -239,6 +274,9 @@ func (database *Database) InsertFolder(folder string) (int64, error) { } func (database *Database) InsertFile(folderId int64, filename string, filesize int64) (int64, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.insertFile.Exec(folderId, filename, filesize) if err != nil { return 0, err @@ -274,6 +312,9 @@ func (database *Database) Insert(path string, filesize int64) (int64, error) { } func (database *Database) UpdateFoldername(folderId int64, foldername string) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.updateFoldername.Exec(foldername, folderId) if err != nil { return err diff --git a/pkg/database/method_feedback.go b/pkg/database/method_feedback.go index 65f7f6c..f63a93d 100644 --- a/pkg/database/method_feedback.go +++ b/pkg/database/method_feedback.go @@ -1,6 +1,9 @@ package database func (database *Database) InsertFeedback(time int64, content string, userID int64, header string) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.insertFeedback.Exec(time, content, userID, header) if err != nil { return err @@ -9,6 +12,9 @@ func (database *Database) InsertFeedback(time int64, content string, userID int6 } func (database *Database) GetFeedbacks() ([]*Feedback, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getFeedbacks.Query() if err != nil { return nil, err @@ -32,6 +38,9 @@ func (database *Database) GetFeedbacks() ([]*Feedback, error) { } func (database *Database) DeleteFeedback(id int64) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.deleteFeedback.Exec(id) if err != nil { return err diff --git a/pkg/database/method_review.go b/pkg/database/method_review.go index 748e3f7..f1ea70b 100644 --- a/pkg/database/method_review.go +++ b/pkg/database/method_review.go @@ -1,6 +1,9 @@ package database func (database *Database) InsertReview(review *Review) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.insertReview.Exec( review.UserId, review.FileId, @@ -10,6 +13,9 @@ func (database *Database) InsertReview(review *Review) error { } func (database *Database) GetReviewsOnFile(fileId int64) ([]*Review, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getReviewsOnFile.Query(fileId) if err != nil { return nil, err @@ -42,6 +48,9 @@ func (database *Database) GetReviewsOnFile(fileId int64) ([]*Review, error) { } func (database *Database) GetReview(reviewId int64) (*Review, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + row := database.stmt.getReview.QueryRow(reviewId) review := &Review{} @@ -60,6 +69,9 @@ func (database *Database) GetReview(reviewId int64) (*Review, error) { } func (database *Database) UpdateReview(review *Review) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.updateReview.Exec( review.Content, review.UpdatedAt, @@ -68,11 +80,17 @@ func (database *Database) UpdateReview(review *Review) error { } func (database *Database) DeleteReview(reviewId int64) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.deleteReview.Exec(reviewId) return err } func (database *Database) GetReviewsByUser(userId int64) ([]*Review, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getReviewsByUser.Query(userId) if err != nil { return nil, err diff --git a/pkg/database/method_tag.go b/pkg/database/method_tag.go index dde1226..d6929e8 100644 --- a/pkg/database/method_tag.go +++ b/pkg/database/method_tag.go @@ -2,19 +2,26 @@ package database import "errors" -func (database *Database) InsertTag(tag *Tag) (*Tag, error) { +func (database *Database) InsertTag(tag *Tag) (int64, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.insertTag.Exec(tag.Name, tag.Description, tag.CreatedByUserId) if err != nil { - return nil, err + return 0, err } id, err := result.LastInsertId() if err != nil { - return nil, err + return 0, err } - return database.GetTag(id) + + return id, nil } func (database *Database) GetTag(id int64) (*Tag, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + tag := &Tag{CreatedByUser: &User{}} err := database.stmt.getTag.QueryRow(id).Scan( &tag.ID, &tag.Name, &tag.Description, @@ -27,6 +34,10 @@ func (database *Database) GetTag(id int64) (*Tag, error) { func (database *Database) GetTags() ([]*Tag, error) { tags := []*Tag{} + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getTags.Query() if err != nil { return nil, err @@ -46,23 +57,30 @@ func (database *Database) GetTags() ([]*Tag, error) { return tags, nil } -func (database *Database) UpdateTag(tag *Tag) (*Tag, error) { +func (database *Database) UpdateTag(tag *Tag) (error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.updateTag.Exec(tag.Name, tag.Description, tag.ID) if err != nil { - return nil, err + return err } rowsAffected, err := result.RowsAffected() if err != nil { - return nil, err + return err } if rowsAffected == 0 { - return nil, errors.New("No rows affected") + return errors.New("No rows affected") } - return database.GetTag(tag.ID) + + return nil } // delete tag and all its references in file_has_tag func (database *Database) DeleteTag(id int64) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + // begin transaction tx, err := database.sqlConn.Begin() if err != nil { diff --git a/pkg/database/method_tag_and_file.go b/pkg/database/method_tag_and_file.go index 2715bbe..6765aa1 100644 --- a/pkg/database/method_tag_and_file.go +++ b/pkg/database/method_tag_and_file.go @@ -1,6 +1,9 @@ package database func (database *Database) PutTagOnFile(tagID, fileID, userID int64) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.putTagOnFile.Exec(tagID, fileID, userID) if err != nil { return err @@ -12,6 +15,9 @@ func (database *Database) PutTagOnFile(tagID, fileID, userID int64) error { } func (database *Database) GetTagsOnFile(fileID int64) ([]*Tag, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getTagsOnFile.Query(fileID) if err != nil { return nil, err @@ -31,6 +37,9 @@ func (database *Database) GetTagsOnFile(fileID int64) ([]*Tag, error) { } func (database *Database) DeleteTagOnFile(tagID, fileID int64) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + result, err := database.stmt.deleteTagOnFile.Exec(tagID, fileID) if err != nil { return err diff --git a/pkg/database/method_user.go b/pkg/database/method_user.go index 6f2a52b..92ff9e5 100644 --- a/pkg/database/method_user.go +++ b/pkg/database/method_user.go @@ -1,6 +1,9 @@ package database func (database *Database) Login(username string, password string) (*User, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + user := &User{} // get user from database @@ -12,6 +15,9 @@ func (database *Database) Login(username string, password string) (*User, error) } func (database *Database) LoginAsAnonymous() (*User, error) { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + user := &User{} // get user from database @@ -38,6 +44,9 @@ func (database *Database) Register(username string, password string, usertype in active = true } + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err = database.stmt.insertUser.Exec(username, password, usertype, active, 0) if err != nil { return err @@ -48,6 +57,9 @@ func (database *Database) Register(username string, password string, usertype in func (database *Database) GetUserById(id int64) (*User, error) { user := &User{} + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + // get user from database err := database.stmt.getUserById.QueryRow(id).Scan(&user.ID, &user.Username, &user.Role, &user.Active, &user.AvatarId) if err != nil { @@ -58,6 +70,10 @@ func (database *Database) GetUserById(id int64) (*User, error) { func (database *Database) CountAdmin() (int64, error) { var count int64 + + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + err := database.stmt.countAdmin.QueryRow().Scan(&count) if err != nil { return 0, err @@ -68,6 +84,9 @@ func (database *Database) CountAdmin() (int64, error) { func (database *Database) GetUsers() ([]*User, error) { users := make([]*User, 0) + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + rows, err := database.stmt.getUsers.Query() if err != nil { return nil, err @@ -86,6 +105,9 @@ func (database *Database) GetUsers() ([]*User, error) { } func (database *Database) UpdateUserActive(id int64, active bool) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.updateUserActive.Exec(active, id) if err != nil { return err @@ -94,6 +116,9 @@ func (database *Database) UpdateUserActive(id int64, active bool) error { } func (database *Database) UpdateUsername(id int64, username string) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.updateUsername.Exec(username, id) if err != nil { return err @@ -102,6 +127,9 @@ func (database *Database) UpdateUsername(id int64, username string) error { } func (database *Database) UpdateUserPassword(id int64, password string) error { + database.singleThreadLock.Lock() + defer database.singleThreadLock.Unlock() + _, err := database.stmt.updateUserPassword.Exec(password, id) if err != nil { return err